Some Examples
Example Data Sets
When working with research data, a first step is usually to read and clean the data.
We'll put that off for a little while and work with some data sets made available in R packages.
Data sets available in R packages include:
many classic data sets;
newer, often larger, data sets useful for learning;
current data obtained by querying web APIs.
Old Faithful Eruptions
A simple classic data set is the geyser data frame available in package MASS.
data(geyser, package = "MASS")
dim(geyser)
## [1] 299 2
head(geyser, 4)
## waiting duration
## 1 80 4.016667
## 2 71 2.150000
## 3 57 4.000000
## 4 80 4.000000
head and tail return the first and last few rows of a data frame.
They are useful for quick sanity checks.
The rows represent measurements recorded for eruptions of the Old Faithful geyser in Yellowstone National Park, Wyoming.
The variables are:
The durations have a bimodal distribution:

ggplot(geyser) +
geom_histogram(aes(x = duration),
bins = 15,
color = "black",
fill = "grey")
This is ggplot code for creating a histogram.
A basic template for creating a plot with ggplot:
ggplot(data = <DATA>) +
<GEOM>(mapping = aes(<MAPPINGS>))
An interesting question is whether the duration can be used to predict when the next eruption will occur.
A plot of the previous duration against the waiting time to the current eruption:

ggplot(geyser) +
geom_point(aes(x = lag(duration),
y = waiting))
It looks like a useful rule would be to expect a shorter waiting time after a shorter eruption duration.
An interesting feature:
Many durations are recorded as 2 or 4 minutes.
This can also be seen in a histogram with a small bin width:

p <- ggplot(geyser) +
geom_histogram(aes(x = duration,
y = stat(density)),
fill = "grey",
color = "black",
binwidth = 0.1)
p
ggplot produces a plot object.
Drawing only happens when the object is printed.
Does this rounding matter?
Taking 3 minutes as the divide between short and long durations we can first pick out the short and long durations:
d <- geyser$duration
d_short <- d[d < 3]
d_long <- d[d >= 3]
Then compute the means and standard deviations as
mean(d_short)
## [1] 1.980317
sd(d_short)
## [1] 0.2779829
mean(d_long)
## [1] 4.262113
sd(d_long)
## [1] 0.3937525
mean(d >= 3)
## [1] 0.6488294
An approach that scales better:
Compute group summaries using tools from the dplyr tidyverse package.
First, add a type variable:
geyser <- mutate(geyser, type = ifelse(duration < 3, "short", "long"))
The summaries can then be computed as
sgd <- summarize(group_by(geyser, type),
mean = mean(duration),
sd = sd(duration),
n = n())
(sgd <- mutate(sgd, prop = n / sum(n)))
## # A tibble: 2 x 5
## type mean sd n prop
## <chr> <dbl> <dbl> <int> <dbl>
## 1 long 4.26 0.394 194 0.649
## 2 short 1.98 0.278 105 0.351
The functions summarize, group_by, and mutate are from the dplyr package that implements a grammar of data manipulation.
This computation can also be written using the forward pipe operator %>%:
sgd <-
group_by(geyser, type) %>%
summarize(mean = mean(duration),
sd = sd(duration),
n = n()) %>%
ungroup() %>%
mutate(prop = n / sum(n))
sgd
## # A tibble: 2 x 5
## type mean sd n prop
## <chr> <dbl> <dbl> <int> <dbl>
## 1 long 4.26 0.394 194 0.649
## 2 short 1.98 0.278 105 0.351
The pipe operator allows a sequence of operations to be chained together.
The left-hand operation is passed implicitly as the first argument to the function called on the right.
One way to show the superimposed normal densities:

f1 <- function(x)
sgd$prop[1] * dnorm(x, sgd$mean[1], sgd$sd[1])
f2 <- function(x)
sgd$prop[2] * dnorm(x, sgd$mean[2], sgd$sd[2])
p <- p +
stat_function(color = "red", fun = f1) +
stat_function(color = "blue", fun = f2)
p
A ggplot can consist of several layers.
The means and standard deviations are affected by the rounding.
Summaries that omit values equal to 2 or 4 minutes can be computed as
geyser2 <- filter(geyser, duration != 2, duration != 4)
sgd2 <-
group_by(geyser2, type) %>%
summarize(mean = mean(duration),
sd = sd(duration),
n = n()) %>%
ungroup() %>%
mutate(prop = n / sum(n))
sgd2
## # A tibble: 2 x 5
## type mean sd n prop
## <chr> <dbl> <dbl> <int> <dbl>
## 1 long 4.36 0.422 141 0.632
## 2 short 1.97 0.315 82 0.368
A plot showing curves computed both ways:

f1_2 <- function(x)
sgd2$prop[1] * dnorm(x, sgd2$mean[1], sgd2$sd[1])
f2_2 <- function(x)
sgd2$prop[2] * dnorm(x, sgd2$mean[2], sgd2$sd[2])
p <- p +
stat_function(color = "red",
linetype = 2,
fun = f1_2) +
stat_function(color = "blue",
linetype = 2,
fun = f2_2)
p
Minnesota Barley Yields
Another classic data set:
Total yield in bushels per acre for 10 varieties at 6 sites in Minnesota in each of two years, 1931 and 1932.
The raw data:
data(barley, package = "lattice")
head(barley)
## yield variety year site
## 1 27.00000 Manchuria 1931 University Farm
## 2 48.86667 Manchuria 1931 Waseca
## 3 27.43334 Manchuria 1931 Morris
## 4 39.93333 Manchuria 1931 Crookston
## 5 32.96667 Manchuria 1931 Grand Rapids
## 6 28.96667 Manchuria 1931 Duluth
Some initial plots:
p1 <- ggplot(barley) + geom_point(aes(x = yield, y = variety))
p2 <- ggplot(barley) + geom_point(aes(x = yield, y = site))
cowplot::plot_grid(p1, p2)

Using color to separate yields in the two years:
p1 <- ggplot(barley) + geom_point(aes(x = yield, y = variety, color = year))
p2 <- ggplot(barley) + geom_point(aes(x = yield, y = site, color = year))
cowplot::plot_grid(p1, p2)

Can we also show site using symbol shape?

ggplot(barley) +
geom_point(aes(x = yield,
y = variety,
color = year,
shape = site))
There is a lot of interference between shape and color.
Larger points may help:

ggplot(barley) +
geom_point(aes(x = yield,
y = variety,
color = year,
shape = site),
size = 2.5)
Jittering may also help:

ggplot(barley) +
geom_point(aes(x = yield,
y = variety,
color = year,
shape = site),
size = 2.5,
position =
position_jitter(
height = 0.15,
width = 0))
Another approach: faceting to produce small multiples.
ggplot(barley) +
geom_point(aes(x = yield,
y = variety,
color = year)) +
facet_wrap(~site, ncol = 2)

Focusing on summaries can help.
A dot plot of average yields for each site and year:

barley_site_year <-
group_by(barley, site, year) %>%
summarize(yield = mean(yield)) %>%
ungroup()
ggplot(barley_site_year) +
geom_point(aes(y = site,
x = yield,
color = year),
size = 3)
Adding lines can help comparing the changes. This is sometimes called a dumbbell chart:

barley_site_year <-
group_by(barley, site, year) %>%
summarize(yield = mean(yield)) %>%
ungroup()
ggplot(barley_site_year) +
geom_line(aes(y = site,
x = yield,
group = site),
color = "darkgrey",
size = 2) +
geom_point(aes(y = site,
x = yield,
color = year),
size = 4)
Another useful approach for showing repeated measurements is a slope graph:
library(ggrepel)
barley_site_year <-
mutate(barley_site_year, year = fct_rev(year))
barley_site_year_1932 <-
filter(barley_site_year, year == "1932")
ggplot(barley_site_year,
aes(x = year, y = yield, group = site)) +
geom_line() +
geom_text_repel(aes(label = site),
data = barley_site_year_1932,
hjust = "left",
direction = "y") +
scale_x_discrete(expand = expansion(mult = c(0.1, .25)),
position = "top") +
labs(x = NULL, y = "Average Yield")

This emphasizes the reversal for Morris.
Bar charts are sometimes used for summaries, but dot plots are usually a better choice.

ggplot(barley_site_year) +
geom_col(aes(x = yield,
y = site,
fill = year),
size = 3,
position = "dodge",
width = .4)
Hair and Eye Color Data
A data set recording the distribution of hair and eye color and sex in 592 statistics students.
HairEyeDF <- as.data.frame(HairEyeColor)
head(HairEyeDF)
## Hair Eye Sex Freq
## 1 Black Brown Male 32
## 2 Brown Brown Male 53
## 3 Red Brown Male 10
## 4 Blond Brown Male 3
## 5 Black Blue Male 11
## 6 Brown Blue Male 50
The data set is available as a cross-tabulation.
as.data.frame converts it to a data frame.
Looking at the distribution of eye color:

eye <-
group_by(HairEyeDF, Eye) %>%
summarize(Freq = sum(Freq)) %>%
ungroup()
ggplot(eye) +
geom_col(aes(x = Eye,
y = Freq),
position = "dodge")
Mapping eye color to bar color in addition to the horizontal axis position can help:

ggplot(eye) +
geom_col(aes(x = Eye,
y = Freq,
fill = Eye),
position = "dodge")
More sensible colors would be nice but require a bit of work:

hazel_rgb <-
col2rgb("brown") * 0.75 + col2rgb("green") * 0.25
hazel <-
do.call(rgb, as.list(hazel_rgb / 255))
cols <-
c(Blue = colorspace::lighten(colorspace::desaturate("blue", 0.3), 0.3),
Green = colorspace::lighten("forestgreen", 0.1),
Brown = colorspace::lighten("brown", 0.0001), ## 0.3?
Hazel = colorspace::lighten(hazel, 0.3))
pb <- ggplot(eye) +
geom_col(aes(x = Eye,
y = Freq,
fill = Eye),
position = "dodge") +
scale_fill_manual(values = cols)
pb
A stacked bar chart can also be useful:

psb <- ggplot(eye) +
geom_col(aes(x = "", y = Freq, fill = Eye), color = "lightgrey") +
scale_fill_manual(values = cols)
psb
Changing to polar coordinates produces a pie chart:

(pp <- psb + coord_polar("y"))
The axis and grid are not helpful; a theme adjustment can remove them:

(pp <- pp + theme_void())
Themes provide a way to customize the non-data components of plots: i.e. titles, labels, fonts, background, grid lines, and legends.
Themes can be used to give plots a consistent customized look.
The ggthemes package provides a number of themes to emulate the style of different publications, for example theme_wsj and theme_economist.
How well do bar charts and pie charts work?

Some questions:
Which plot makes it easier to tell whether the proportion of brown-eyed students is larger or smaller than the proportion of blue-eyed students?
Which plot makes it easier to tell whether these proportions are larger or smaller than 1/2 or 1/4 or 1/3?
Looking at the proportions within hair color and sex:
eye_hairsex <-
group_by(HairEyeDF, Hair, Sex) %>%
mutate(Prop = Freq / sum(Freq)) %>%
ungroup()
p1 <- ggplot(eye_hairsex) +
geom_col(aes(x = Eye, y = Prop, fill = Eye)) +
scale_fill_manual(values = cols) +
facet_grid(Hair ~ Sex)
p2 <- ggplot(eye_hairsex) +
geom_col(aes(x = "", y = Prop, fill = Eye)) +
scale_fill_manual(values = cols) +
coord_polar("y") +
facet_grid(Hair ~ Sex) +
theme_void()
cowplot::plot_grid(p1, p2)

A more complete ggplot template
ggplot(data = <DATA>) +
<GEOM>(mapping = aes(<MAPPINGS>),
stat = <STAT>,
position = <POSITION>) +
< ... MORE GEOMS ... > +
<COORDINATE_ADJUSTMENT> +
<SCALE_ADJUSTMENT> +
<FACETING> +
<THEME_ADJUSTMENT>
More Examples
These examples start with raw data as you might receive it from a researcher, and involve reading and cleaning the data.
Common data formats you might encounter include
Tools are available for reading data in these formats into R.
Wind Turbines in Iowa
There are many wind turbines in Iowa.
Data is available from the U.S. Wind Turbine Database.
A snapshot is available is here as a CSV file.
CSV files are a common form of data exchange.
They are simple text files that are intended to be written and read by a computer.
Some CSV files include a header and a footer that need to he handled.
One issue is that a comma isn't a good separator in countries where it is the decimal separator!
A CSV file can be read using read.csv or readr::read_csv.
Reading the wind turbine data:
wind_turbines <- read.csv(here::here("data/us_wind.csv"), comment = "#")
Some data cleaning is needed.
Focus on the wind turbines in IOWA (19 is the state component of the FIPS county code for Iowa):
wt_IA <- filter(wind_turbines, t_fips %/% 1000 == 19)
Drop entries with missing longitude or latitude values:
wt_IA <- filter(wt_IA, ! is.na(xlong), ! is.na(ylat))
Some missing year values are encoded as -9999; replace these with NA:
wt_IA <- mutate(wt_IA, p_year = replace(p_year, p_year < 0, NA))
To show the locations of wind turbines on a map, load some map data:
iowa_sf <-
sf::st_as_sf(maps::map("county", "iowa",
plot = FALSE,
fill = TRUE))
p <- ggplot() +
geom_sf(data = iowa_sf) +
ggthemes::theme_map()
p

Locations for all wind turbines in iowa:
p + geom_point(aes(xlong, ylat),
data = wt_IA)

Using color to show when the wind turbines were built:
year_brk <- c(0, 2005, 2010, 2015, 2020)
year_lab <- c("before 2005",
"2005-2009",
"2010-2014",
"2015-2020")
wt_IA <-
mutate(wt_IA,
year = cut(p_year,
breaks = year_brk,
labels = year_lab,
right = FALSE))
p + geom_point(aes(xlong,
ylat,
color = year),
data = wt_IA,
size = 3)

Cancer Map
The website http://www.cancer-rates.info/ia provides data on cancer incidence for a number of different cancers in Iowa.
The data for lung and bronchus cancer in 2011 are available in a csv file in the project.
We can read the file with read_csv from the readr package.
Looking at the file shows some things that need to be cleaned up:
The header can be handled by using skip = 2 in the read_csv call:
fname <- here::here("data/Invasive-Cancer-Incidence-Rates-by-County-in-Iowa-Lung-and-Bronchus-2011.csv")
d <- read_csv(fname, skip = 2)
head(d)
## # A tibble: 6 x 7
## County `Population at … Cases `Crude Rate` `Age-adjusted R… `95% Confidence …
## <chr> <dbl> <chr> <chr> <chr> <chr>
## 1 Union 12570 20 159.11 115.82 69.86
## 2 Ringgo… 5098 11 215.77 115.03 54.48
## 3 Monroe 8044 12 149.18 109.99 56.24
## 4 Page 15926 25 156.98 109.20 70.12
## 5 Montgo… 10655 17 159.55 99.45 57.41
## 6 Adams 3996 6 150.15 95.84 35.14
## # … with 1 more variable: 95% Confidence Interval-Upper Limit <chr>
Let's focus on a few variables and give them more convenient names:
d <- select(d, county = 1, population = 2, count = 3)
The footer needs to be removed:
tail(d)
## # A tibble: 6 x 3
## county population count
## <chr> <dbl> <chr>
## 1 Butler 14960 5
## 2 Winneshiek 21045 ~
## 3 STATE 3065223 2368
## 4 Note: All rates are per 100,000. Rates are age-adjusted to t… NA <NA>
## 5 Rates generated on Jun 12, 2019. NA <NA>
## 6 Based on data released Nov 2017. NA <NA>
One way to remove the footer:
d <- filter(d, ! is.na(population))
d <- filter(d, county != "STATE")
Changing count to numeric changes the ~ entries to missing values (NA) values:
d <- mutate(d, count = as.numeric(count))
In this case there are no zero case values; two ways to check:
count(d, count == 0)
## # A tibble: 2 x 2
## `count == 0` n
## <lgl> <int>
## 1 FALSE 95
## 2 NA 4
any(d$count == 0, na.rm = TRUE)
## [1] FALSE
It might be reasonable to assume these values where zero, so replace them with zeros:
d <- replace_na(d, list(count = 0))
A choropleth map uses color or shading to represent values measured for different geographic regions.
We will need to merge, or left join, the cancer data with the map date we loaded for the wind turbine map.
This requires a key on which to match the records in the cancer data and the map data.
For Iowa this can be done with the county name, but some care is needed:
d$county[1]
## [1] "Union"
iowa_sf$ID[1]
## [1] "iowa,adair"
Fixing case differences and dropping the iowa, prefix:
d <- mutate(d, cname = county, county = tolower(county))
iowa_sf <- mutate(iowa_sf, ID = sub("iowa,", "", ID))
iowa_sf <- rename(iowa_sf, county = ID)
Still not quite there:
setdiff(d$county, iowa_sf$county)
## [1] "o'brien"
setdiff(iowa_sf$county, d$county)
## [1] "obrien"
Drop the apostrophe in O'Brien:
d <- mutate(d, county = sub("'", "", county))
setdiff(d$county, iowa_sf$county)
## character(0)
setdiff(iowa_sf$county, d$county)
## character(0)
Define rate1K variable as the number of cases per 1000 inhabitants and left join the cancer data to the map data:
d <- mutate(d, rate1K = 1000 * (count / population))
md <- left_join(iowa_sf, d, "county")
head(md)
## Simple feature collection with 6 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -95.10526 ymin: 40.60552 xmax: -91.06018 ymax: 43.51041
## CRS: EPSG:4326
## county population count cname rate1K geom
## 1 adair 7565 10 Adair 1.3218771 MULTIPOLYGON (((-94.24583 4...
## 2 adams 3996 6 Adams 1.5015015 MULTIPOLYGON (((-94.70992 4...
## 3 allamakee 14204 7 Allamakee 0.4928189 MULTIPOLYGON (((-91.22634 4...
## 4 appanoose 12863 11 Appanoose 0.8551660 MULTIPOLYGON (((-92.63009 4...
## 5 audubon 6019 5 Audubon 0.8307028 MULTIPOLYGON (((-95.10526 4...
## 6 benton 26121 28 Benton 1.0719345 MULTIPOLYGON (((-92.06286 4...
A simple map:
ggplot(md) +
geom_sf(aes(fill = rate1K))

An improved version:
library(ggthemes)
library(viridis)
ggplot(md) +
geom_sf(aes(fill = rate1K),
color = "grey") +
scale_fill_viridis(name = "Rate per 1000") +
theme_map()

A simple interactive version using plotly:
mdl <- mutate(md,
label = paste(cname,
round(rate1K, 1),
population,
sep = "\n"))
p <- ggplot(mdl) +
geom_sf(aes(fill = rate1K,
text = label),
color = "grey") +
scale_fill_viridis(name = "Rate per 1000") +
theme_map()
plotly::ggplotly(p, tooltip = "text")
The leaflet package supports more sophisticated interactive maps:
library(leaflet)
pal <- colorNumeric(palette = "viridis", domain = md$rate1K)
lab <- lapply(paste0(md$cname, "<BR>",
"Rate: ", round(md$rate1K, 1), "<BR>",
"Pop: ", scales::comma(md$population,
accuracy = 1)),
htmltools::HTML)
leaflet(sf::st_transform(md, 4326)) %>%
addPolygons(weight = 2,
color = "grey",
fillColor = ~ pal(rate1K),
fillOpacity = 1,
highlightOptions = highlightOptions(color = "white",
weight = 2,
bringToFront = TRUE),
label = lab) %>%
addLegend(pal = pal, values = ~ rate1K, opacity = 1)
Unemployment Map
Local Area Unemployment Statistics page from the Bureau of Labor Statistics makes available county-level monthly unemployment data for a 14-month window.
The file for February 2020 through March 2021 is available is available at http://www.stat.uiowa.edu/~luke/data/laus/laucntycur14-2020.txt and in the project data folder.
This file is a text file but uses a non-standard separator.
It is designed for human readability and uses a comma as a thousands separator or grouping mark.
It also includes header and footer information.
It is still reasonably easy to read in.
One way to read the data into R is:
lausURL <- here::here("data/laucntycur14-2020.txt")
lausUS <- read.table(lausURL,
col.names = c("LAUSAreaCode", "State", "County",
"Title", "Period",
"LaborForce", "Employed",
"Unemployed", "UnempRate"),
quote = '"', sep = "|", skip = 6,
stringsAsFactors = FALSE, strip.white = TRUE,
fill = TRUE)
footstart <- grep("------", lausUS$LAUSAreaCode)
lausUS <- lausUS[1 : (footstart - 1), ]
It may be useful to be able to access the county name and state name separately:
lausUS <- separate(lausUS, Title, c("cname", "scode"),
sep = ", ", fill = "right")
Check the variable types:
sapply(lausUS, class)
## LAUSAreaCode State County cname scode Period
## "character" "integer" "integer" "character" "character" "character"
## LaborForce Employed Unemployed UnempRate
## "character" "character" "character" "character"
The UnempRate variable is read as character data because of missing value encoding, so needs to be converted to numeric:
lausUS <- mutate(lausUS, UnempRate = as.numeric(UnempRate))
Check for missing values:
select_if(lausUS, anyNA) %>% names()
## [1] "scode" "UnempRate"
The state code is missing for the District of Columbia:
select(lausUS, cname, scode) %>%
filter(is.na(scode)) %>%
unique()
## cname scode
## 1 District of Columbia <NA>
March and April 2020 numbers were not available for Puerto Rico:
select(lausUS, scode, Period, UnempRate) %>%
filter(is.na(UnempRate)) %>%
unique()
## scode Period UnempRate
## 1 PR Mar-20 NA
## 79 PR Apr-20 NA
To compute the national monthly unemployment rates over this period we need some more data cleaning:
lausUS <- mutate(lausUS,
Period = fct_inorder(Period),
LaborForce = as.numeric(gsub(",", "", LaborForce)),
Unemployed = as.numeric(gsub(",", "", Unemployed)))
Unemployment during this period was affected significantly by the COVID-19 pandemic.
A plot shows a large spike in April 2020:
group_by(lausUS, Period) %>%
summarize(Unemployed = sum(Unemployed, na.rm = TRUE),
LaborForce = sum(LaborForce, na.rm = TRUE),
UnempRate = 100 * (Unemployed / LaborForce)) %>%
ggplot(aes(Period, UnempRate, group = 1)) +
geom_line()

A choropleth map can be used to look at how the impact was distributed across the country.
To show unemployment rates on a map we need to merge the unemployment data with map data.
To match county unemployment data and county shape data it is safer to use the numeric FIPS county code. This can be added with
lausUS <- mutate(lausUS, fips = State * 1000 + County)
Shape data for US counties can be obtained from a number of sources in a number of different formats.
Here is one approach:
counties_sf <- sf::st_as_sf(maps::map("county", plot = FALSE, fill = TRUE))
county.fips <-
mutate(maps::county.fips, polyname = sub(":.*", "", polyname)) %>%
unique()
counties_sf <- left_join(counties_sf, county.fips, c("ID" = "polyname"))
states_sf <- sf::st_as_sf(maps::map("state", plot = FALSE, fill = TRUE))
Some summaries over the period can be computed as
summaryUS <- group_by(lausUS, County, State, fips) %>%
summarize(avg_unemp = mean(UnempRate, na.rm = TRUE),
max_unemp = max(UnempRate, na.rm = TRUE),
apr_unemp = UnempRate[Period == "Apr-20"]) %>%
ungroup()
## `summarise()` has grouped output by 'County', 'State'. You can override using the `.groups` argument.
head(summaryUS)
## # A tibble: 6 x 6
## County State fips avg_unemp max_unemp apr_unemp
## <int> <int> <dbl> <dbl> <dbl> <dbl>
## 1 1 1 1001 4.65 10.9 10.9
## 2 1 4 4001 12.7 17.6 16.6
## 3 1 5 5001 4.06 5.2 4.8
## 4 1 6 6001 8.8 14.6 14.6
## 5 1 8 8001 8.3 12.7 12.7
## 6 1 9 9001 8.31 11.7 8.2
A choropleth map of the April 2020 unemployment rates:
left_join(counties_sf, summaryUS, "fips") %>%
ggplot() +
geom_sf(aes(fill = apr_unemp)) +
scale_fill_viridis(name = "Rate", na.value = "red") +
theme_map() +
geom_sf(data = states_sf, col = "grey", fill = NA)

Using a very visible color for missing data is useful, at least during exploration.
anti_join can show the county geometry that does not have an entry in the unemployment data:
anti_join(counties_sf, summaryUS, "fips")
## Simple feature collection with 1 feature and 2 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -103.0121 ymin: 42.99475 xmax: -102.0782 ymax: 43.68803
## CRS: EPSG:4326
## ID fips geom
## 1 south dakota,shannon 46113 MULTIPOLYGON (((-102.8115 4...
Shannon County, SD (FIPS 46113), was renamed to Oglala Lakota County in June 2015 and given a new FIPS code, 46102.
The geometry data table needs to be updated:
counties_sf <- mutate(counties_sf, fips = replace(fips, fips == 46113, 46102))
With the updated data the map is now complete:
left_join(counties_sf, summaryUS, "fips") %>%
ggplot() +
geom_sf(aes(fill = apr_unemp)) +
scale_fill_viridis(name = "Rate", na.value = "red") +
theme_map() +
geom_sf(data = states_sf, col = "grey", fill = NA)

Gapminder Childhood Mortality Data
The gapminder package provides a subset of the data from the Gapminder web site.
Additional data sets are available.
A data set on childhood mortality is available locally as a csv file or an Excel file.
The Excel file is also available in the project data folder.
The numbers represent number of deaths within the first five years per 1000 births.
Many researchers like to manage their data in a spreadsheet.
Being able to read such a sheet directly greatly helps keeping the workflow reproducible.
Many spreadsheets contain header, footers, and other annotations to aid a human viewer.
- As long as the data are in a rectangular region it is usually not hard to extract them programmatically.
Loading the data:
library(readxl)
gcm <- read_excel(here::here("data/gapminder-under5mortality.xlsx"))
A first look:
head(gcm, 3)
## # A tibble: 3 x 217
## `Under five mortality` `1800.0` `1801.0` `1802.0` `1803.0` `1804.0` `1805.0`
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Abkhazia NA NA NA NA NA NA
## 2 Afghanistan 469. 469. 469. 469. 469. 469.
## 3 Akrotiri and Dhekelia NA NA NA NA NA NA
## # … with 210 more variables: 1806.0 <dbl>, 1807.0 <dbl>, 1808.0 <dbl>,
## # 1809.0 <dbl>, 1810.0 <dbl>, 1811.0 <dbl>, 1812.0 <dbl>, 1813.0 <dbl>,
## # 1814.0 <dbl>, 1815.0 <dbl>, 1816.0 <dbl>, 1817.0 <dbl>, 1818.0 <dbl>,
## # 1819.0 <dbl>, 1820.0 <dbl>, 1821.0 <dbl>, 1822.0 <dbl>, 1823.0 <dbl>,
## # 1824.0 <dbl>, 1825.0 <dbl>, 1826.0 <dbl>, 1827.0 <dbl>, 1828.0 <dbl>,
## # 1829.0 <dbl>, 1830.0 <dbl>, 1831.0 <dbl>, 1832.0 <dbl>, 1833.0 <dbl>,
## # 1834.0 <dbl>, 1835.0 <dbl>, 1836.0 <dbl>, 1837.0 <dbl>, 1838.0 <dbl>,
## # 1839.0 <dbl>, 1840.0 <dbl>, 1841.0 <dbl>, 1842.0 <dbl>, 1843.0 <dbl>,
## # 1844.0 <dbl>, 1845.0 <dbl>, 1846.0 <dbl>, 1847.0 <dbl>, 1848.0 <dbl>,
## # 1849.0 <dbl>, 1850.0 <dbl>, 1851.0 <dbl>, 1852.0 <dbl>, 1853.0 <dbl>,
## # 1854.0 <dbl>, 1855.0 <dbl>, 1856.0 <dbl>, 1857.0 <dbl>, 1858.0 <dbl>,
## # 1859.0 <dbl>, 1860.0 <dbl>, 1861.0 <dbl>, 1862.0 <dbl>, 1863.0 <dbl>,
## # 1864.0 <dbl>, 1865.0 <dbl>, 1866.0 <dbl>, 1867.0 <dbl>, 1868.0 <dbl>,
## # 1869.0 <dbl>, 1870.0 <dbl>, 1871.0 <dbl>, 1872.0 <dbl>, 1873.0 <dbl>,
## # 1874.0 <dbl>, 1875.0 <dbl>, 1876.0 <dbl>, 1877.0 <dbl>, 1878.0 <dbl>,
## # 1879.0 <dbl>, 1880.0 <dbl>, 1881.0 <dbl>, 1882.0 <dbl>, 1883.0 <dbl>,
## # 1884.0 <dbl>, 1885.0 <dbl>, 1886.0 <dbl>, 1887.0 <dbl>, 1888.0 <dbl>,
## # 1889.0 <dbl>, 1890.0 <dbl>, 1891.0 <dbl>, 1892.0 <dbl>, 1893.0 <dbl>,
## # 1894.0 <dbl>, 1895.0 <dbl>, 1896.0 <dbl>, 1897.0 <dbl>, 1898.0 <dbl>,
## # 1899.0 <dbl>, 1900.0 <dbl>, 1901.0 <dbl>, 1902.0 <dbl>, 1903.0 <dbl>,
## # 1904.0 <dbl>, 1905.0 <dbl>, …
This data set is in wide format, with one column per year.
A long version with a year and a value column is useful for working with ggplot.
A better first variable name:
names(gcm)[1] <- "country"
Convert to long format:
tgcm <-
pivot_longer(gcm, -1, names_to = "year", values_to = "u5mort") %>%
mutate(year = as.numeric(year))
head(tgcm, 3)
## # A tibble: 3 x 3
## country year u5mort
## <chr> <dbl> <dbl>
## 1 Abkhazia 1800 NA
## 2 Abkhazia 1801 NA
## 3 Abkhazia 1802 NA
Some explorations:
p <- ggplot(tgcm) +
geom_line(aes(year,
u5mort,
group = country),
alpha = 0.3)
plotly::ggplotly(p)
Some selected countries:
countries <- c("United States",
"United Kingdom",
"Germany",
"China",
"Egypt")
filter(tgcm, country %in% countries) %>%
ggplot() +
geom_line(aes(x = year,
y = u5mort,
color = country))

Examining the missing values:
tgcm_miss <-
group_by(tgcm, country) %>%
summarize(anyNA = anyNA(u5mort)) %>%
filter(anyNA) %>%
pull(country)
p <- filter(tgcm,
country %in% tgcm_miss) %>%
ggplot(aes(x = year,
y = u5mort,
group = country)) +
geom_line(na.rm = TRUE) +
xlim(c(1940, 2020))
plotly::ggplotly(p)
LS0tCnRpdGxlOiAiQmFzaWMgRGF0YSBXcmFuZ2xpbmcgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSIgphdXRob3I6ICJMdWtlIFRpZXJuZXkiCmluc3RpdHV0ZTogIlVuaXZlcnNpdHkgb2YgSW93YSIKZGF0ZTogIjIxIEp1bmUsIDIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSkKeGFyaW5nYW5FeHRyYTo6dXNlX2NsaXBib2FyZCgpCmxpYnJhcnkoZ2dwbG90MikKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSArCiAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsKICAgICAgICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmV5MzAiLCBmaWxsID0gTkEpKSkKaGVyZV9yZWwgPC0gZnVuY3Rpb24ocGF0aCkKICAgIGlmIChmaWxlLmV4aXN0cyhwYXRoKSkgcGF0aCBlbHNlIGZpbGUucGF0aCgiLi4iLCBwYXRoKQpgYGAKCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KLmNvbnRlbnQtYm94LWJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGJsdWU7IH0KLm5vdGUgewoJcGFkZGluZzogMTVweDsKCW1hcmdpbi1ib3R0b206IDIwcHg7Cglib3JkZXI6IDFweCBzb2xpZCB0cmFuc3BhcmVudDsKCWJvcmRlci1yYWRpdXM6IDRweDsKCWJhY2tncm91bmQtY29sb3I6ICNkOWVkZjc7Cglib3JkZXItY29sb3I6ICNiY2U4ZjE7Cgljb2xvcjogIzMxNzA4ZjsKfQpgYGAKCgojIyBJbnRyb2R1Y3Rpb24KCiMjIyBPdXRsaW5lCgpJbiB0aGlzIGNsYXNzIEkgd2lsbAoKKiBCcmllZmx5IG91dGxpbmUgdGhlIGhpc3Rvcnkgb2YgUi4KCiogVXNpbmcgc29tZSBleGFtcGxlcyBicmllZmx5IHNob3cgaG93IHRvIGRvIGRhdGEgd3JhbmdsaW5nCiAgYW5kIHZpc3VhbGl6ZSBkYXRhIGluIFIuCgpNYXRlcmlhbHMgZm9yIHRoaXMgY2xhc3MgYXJlIGF2YWlsYWJsZSBvbiBHaXRIdWIgYXQKPGh0dHBzOi8vZ2l0aHViLmNvbS9sdGllcm5leS9TSUJTLVdWLTIwMjEuZ2l0Pi4KCiogWW91IGNhbiBhY2Nlc3MgaXQgYXMgYW4gUlN0dWRpbyBwcm9qZWN0IGJ5IGZvbGxvd2luZyB0aGUgbWVudSBzZWxlY3Rpb24KICAqKkZpbGUgPiBOZXcgUHJvamVjdCA+IFZlcnNpb24gQ29udHJvbCA+IEdpdCoqIGFuZCBzcGVjaWZ5aW5nIHRoaXMgVVJMLgoKKiBZb3UgY2FuIHVzZSB0aGUgYGdpdGAgY29tbWFuZCBsaW5lIGNsaWVudCB3aXRoCiAgICBgYGBzaGVsbApnaXQgY2xvbmUgaHR0cHM6Ly9naXRodWIuY29tL2x0aWVybmV5L1NJQlMtV1YtMjAyMS5naXQKICAgIGBgYAoKTWF0ZXJpYWxzIGZvciBvdXIgX0RhdGEgVmlzdWFsaXphdGlvbiBhbmQgRGF0YQpUZWNobm9sb2dpZXNfIGNvdXJzZSBhcmUgYXZhaWxhYmxlIGF0Cgo8aHR0cDovL3d3dy5zdGF0LnVpb3dhLmVkdS9+bHVrZS9jbGFzc2VzL1NUQVQ0NTgwLTIwMjEvPgoKCiMjIyBUb29scwoKU29tZSB0b29scyBJIHdpbGwgYmUgdXNpbmc6CgoqIFRoZSBbUlN0dWRpb10oaHR0cHM6Ly93d3cucnN0dWRpb24uY29tKSBJREUuCgoqIE1hbnkgZmVhdHVyZXMgZnJvbSB0aGUgYmFzaWMgW1JdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcpIGRpc3RyaWJ1dGlvbi4KCiogU29tZSB0b29scyBmcm9tIHRoZSBbX3RpZHl2ZXJzZV9dKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKS4KCiogVGhlIFtgZ2dwbG90YF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGJhc2VkIG9uCiAgdGhlIF9HcmFtbWFyIG9mIEdyYXBoaWNzXyBmcmFtZXdvcmsuCgpNb3N0IG9mIHRoZSBwYWNrYWdlcyBhcmUgbG9hZGVkIGJ5IGxvYWRpbmcgdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2U6CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMgUmVmZXJlbmNlcwoKVXNlZnVsIHJlZmVyZW5jZXM6Cgo+IEhhZGxleSBXaWNraGFtIGFuZCBHYXJyZXR0IEdyb2xlbXVuZCAoMjAxNiksIFtfUiBmb3IgRGF0YQo+IFNjaWVuY2VfXShodHRwOi8vcjRkcy5oYWQuY28ubnovKSwgTydSZWlsbHkuCgo+IENsYXVzIE8uIFdpbGtlICgyMDE5KSwgW19GdW5kYW1lbnRhbHMgb2YgRGF0YQo+ICBWaXN1YWxpemF0aW9uX10oaHR0cHM6Ly9zZXJpYWxtZW50b3IuY29tL2RhdGF2aXovKSwgTydSZWlsbHkuCgo+IEtpZXJhbiBIZWFseSAoMjAxOCkgW19EYXRhIFZpc3VhbGl6YXRpb246IEEgcHJhY3RpY2FsCj4gaW50cm9kdWN0aW9uX10oaHR0cDovL3NvY3Zpei5jby8pLCBQcmluY2V0b24KCj4gUmFmYWVsIEEuIElyaXphcnJ5ICgyMDE5KSwgW0ludHJvZHVjdGlvbiB0byBEYXRhIFNjaWVuY2U6IF9EYXRhCj4gQW5hbHlzaXMgYW5kIFByZWRpY3Rpb24gQWxnb3JpdGhtcyB3aXRoCj4gUl9dKGh0dHBzOi8vcmFmYWxhYi5naXRodWIuaW8vZHNib29rLyksIENoYXBtYW4gJiBIYWxsL0NSQy4gKFtCb29rCj4gc291cmNlIG9uIEdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL3JhZmFsYWIvZHNib29rKSkKCioqQXNrIHF1ZXN0aW9ucyBhbnkgdGltZSEqKgoKIAojIyBUaGUgUiBMYW5ndWFnZQoKIyMjIEJhY2tncm91bmQKClIgaXMgYSBsYW5ndWFnZSBmb3IgZGF0YSBhbmFseXNpcyBhbmQgZ3JhcGhpY3MuCgoqIFIgd2FzIG9yaWdpbmFsbHkgZGV2ZWxvcGVkIGJ5IFJvYmVydCBHZW50bGVtYW4gYW5kIFJvc3MgSWhha2EgaW4gdGhlCiAgZWFybHkgMTk5MCdzIGZvciBhIE1hY2ludG9zaCBjb21wdXRlciBsYWIgYXQgVS4gb2YgQXVja2xhbmQsIE5ldyBaZWFsYW5kLgoKKiBSIGlzIGJhc2VkIG9uIHRoZSBTIGxhbmd1YWdlIGRldmVsb3BlZCBieSBKb2huIENoYW1iZXJzIGFuZAogIG90aGVycyBhdCBCZWxsIExhYnMuCgpSIGlzIGFuIE9wZW4gU291cmNlIHByb2plY3QuCgoqIFNpbmNlIDE5OTcgUiBpcyBkZXZlbG9wZWQgYW5kIG1haW50YWluZWQgYnkgdGhlIFItY29yZSBncm91cCwKICB3aXRoIGFyb3VuZCAyMCBtZW1iZXJzIGxvY2F0ZWQgaW4gbWFvciB0aGFuIDEwIGRpZmZlcmVudCBjb3VudHJpZXMuCgoqIFIgaXMgd2lkZWx5IHVzZWQgaW4gdGhlIGZpZWxkIG9mIHN0YXRpc3RpY3MgYW5kIGJleW9uZCwgZXNwZWNpYWxseSBpbgogIHVuaXZlcnNpdHkgZW52aXJvbm1lbnRzLgoKKiBSIGhhcyBiZWNvbWUgdGhlIHByaW1hcnkgZnJhbWV3b3JrIGZvciBkZXZlbG9waW5nIGFuZCBtYWtpbmcgYXZhaWxhYmxlCiAgbmV3IHN0YXRpc3RpY2FsIG1ldGhvZG9sb2d5LgoKKiBNYW55IChub3cgb3ZlciAxNywwMDApIGV4dGVuc2lvbiBwYWNrYWdlcyBhcmUgYXZhaWxhYmxlIHRocm91Z2ggQ1JBTiBvcgogIHNpbWlsYXIgcmVwb3NpdG9yaWVzLgoKIyMjIFdvcmtpbmcgd2l0aCBSCgpSIGlzIGRlc2lnbmVkIGZvciBpbnRlcmFjdGl2ZSBkYXRhIGV4cGxvcmF0aW9uLgoKKiBJbnRlcmFjdGlvbiBpcyB0aHJvdWdoIGEgX3JlYWQtZXZhbC1wcmludCBsb29wIChSRVBMKV8uCgoqIFRoaXMgaXMgYWxzbyBjYWxsZWQgYSBfY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKV8uCgpBbGwgY29tcHV0YXRpb25zIGFyZSBzcGVjaWZpZWQgaW4gdGhlIFIgbGFuZ3VhZ2UuCgoqIEV2ZW4gZm9yIHNpbXBsZSB0YXNrcyB5b3UgbmVlZCB0byBrbm93IGEgbGl0dGxlIG9mIHRoZSBsYW5ndWFnZS4KCiogQWZ0ZXIgbGVhcm5pbmcgdG8gZG8gc2ltcGxlIHRhc2tzIHlvdSBrbm93IHNvbWUgb2YgdGhlIGxhbmd1YWdlLgoKVGhlIGxhbmd1YWdlIGlzIHVzZWQgdG8KCiogcHJlcGFyZSBkYXRhIGZvciBhbmFseXNpczsKCiogc3BlY2lmeSBpbmRpdmlkdWFsIGFuYWx5c2VzOwoKKiBwcm9ncmFtIHJlcGVhdGVkIG9yIHNpbWlsYXIgYW5hbHlzZXM7CgoqIHByb2dyYW0gbmV3IG1ldGhvZHMgb2YgYW5hbHlzaXMuCgpTcGVjaWZ5aW5nIHRoZXNlIHRhc2tzIGluIGEgbGFuZ3VhZ2Ugc3VwcG9ydHMgX3JlcHJvZHVjaWJsZSByZXNlYXJjaF8uCgpUaGUgUiBsYW5ndWFnZSBvcGVyYXRlcyBvbiB2ZWN0b3JzIGFuZCBhcnJheXMuCgpDb21tb25seSB1c2VkIGRhdGEgdHlwZXMgYXJlOgoKKiBpbnRlZ2VyIGFuZCBudW1lcmljIHZlY3RvcnM7CgoqIGxvZ2ljYWwgdmVjdG9yczsKCiogY2hhcmFjdGVyIHZlY3RvcnM7CgoqIGZhY3RvcnMuCgpBbGwgYmFzaWMgdmVjdG9yIHR5cGVzIHN1cHBvcnQgbWlzc2luZyAoYE5BYCkgdmFsdWVzLgoKQXJpdGhtZXRpYyBvcGVyYXRpb25zIGFyZSB2ZWN0b3JpemVkIHRvIG9wZXJhdGUgZWxlbWVudC13aXNlIG9uIHZlY3RvcnMuCgpEYXRhIHZlY3RvcnMgYXJlIHVzdWFsbHkgY29tYmluZWQgaW50byB0YWJsZS1saWtlIG9iamVjdHMgY2FsbGVkIF9kYXRhCmZyYW1lc18uCgoKIyMjIFRoZSBEYXRhIEFuYWx5c2lzIFByb2Nlc3MKCkEgZmlndXJlIHRoYXQgc2hvd3MgdGhlIHN0ZXBzIHVzdWFsbHkgaW52b2x2ZWQgaW4gYSBkYXRhIGFuYWx5c2lzCnByb2plY3Q6CgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KG5vbW5vbWwpCmBgYAo8Y2VudGVyPgpgYGB7bm9tbm9tbCwgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gOH0KI3BhZGRpbmc6IDI1CiNmb250c2l6ZTogMTgKI2ZpbGw6ICNFMURBRkY7ICNENEE5RkYKI3N0cm9rZTogIzg1MTVDNwojbGluZXdpZHRoOiAyCgpbSW1wb3J0XSAtPiBbVW5kZXJzdGFuZF0KW1VuZGVyc3RhbmQgfAogIFtXcmFuZ2xlXSAtPiBbVmlzdWFsaXplXQogIFtWaXN1YWxpemVdIC0+IFtNb2RlbF0KICBbTW9kZWxdIC0+IFtXcmFuZ2xlXQpdCltVbmRlcnN0YW5kXSAtPiBbQ29tbXVuaWNhdGVdCmBgYAo8L2NlbnRlcj4KClRoZXNlIHN0ZXBzIGFyZSBvZnRlbiByZXBlYXRlZCBtYW55IHRpbWVzLCBzbyBpdCBpcyBpbXBvcnRhbnQgdG8gbWFrZQp5b3VyIHdvcmsgcmVwcm9kdWNpYmxlLgoKCiMjIyBSZXByb2R1Y2libGUgRGF0YSBBbmFseXNpcwoKTWFraW5nIHlvdXIgd29yayByZXByb2R1Y2libGU6CgoqIFNhdmUgeW91IHdvcmsgaW4gYSB0ZXh0IGZpbGUgb3Igbm90ZWJvb2suCgoqIFRyYWNrIGNoYW5nZXMgdG8geW91ciBmaWxlcyB3aXRoIGEgdmVyc2lvbiBjb250cm9sIHN5c3RlbSBsaWtlCiAgW2BnaXRgXShodHRwczovL2dpdC1zY20uY29tLykuCgoqIFVzZSBhIHN5c3RlbSBsaWtlIFtSbWFya2Rvd25dKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSB0bwogIHByZXBhcmUgeW91ciByZXBvcnRzLgoKVGhpcyBhbGxvd3MgeW91IHRvIHJlLWNyZWF0ZSB5b3VyIHJlcG9ydCB3aGVuIGRhdGEgY2hhbmdlcyAoYXMgaXQKb2Z0ZW4gd2lsbCEpLgoKQSBnb29kIHJlc291cmNlIGZvciBzZXR0aW5nIHVwIHlvdXIgdG9vbHMgdG8gc3VwcG9ydCB0aGlzIGlzIFtfSGFwcHkKR2l0IGFuZCBHaXRIdWIgZm9yIHRoZSB1c2VSX10oaHR0cHM6Ly9oYXBweWdpdHdpdGhyLmNvbS8pLgoKCiMjIFNvbWUgRXhhbXBsZXMKCiMjIyBFeGFtcGxlIERhdGEgU2V0cwoKV2hlbiB3b3JraW5nIHdpdGggcmVzZWFyY2ggZGF0YSwgYSBmaXJzdCBzdGVwIGlzIHVzdWFsbHkgdG8gcmVhZCBhbmQKY2xlYW4gdGhlIGRhdGEuCgpXZSdsbCBwdXQgdGhhdCBvZmYgZm9yIGEgbGl0dGxlIHdoaWxlIGFuZCB3b3JrIHdpdGggc29tZSBkYXRhIHNldHMKbWFkZSBhdmFpbGFibGUgaW4gUiBwYWNrYWdlcy4KCkRhdGEgc2V0cyBhdmFpbGFibGUgaW4gUiBwYWNrYWdlcyBpbmNsdWRlOgoKKiBtYW55IGNsYXNzaWMgZGF0YSBzZXRzOwoKKiBuZXdlciwgb2Z0ZW4gbGFyZ2VyLCBkYXRhIHNldHMgdXNlZnVsIGZvciBsZWFybmluZzsKCiogY3VycmVudCBkYXRhIG9idGFpbmVkIGJ5IHF1ZXJ5aW5nIHdlYiBBUElzLgoKCiMjIyBPbGQgRmFpdGhmdWwgRXJ1cHRpb25zCgpBIHNpbXBsZSBjbGFzc2ljIGRhdGEgc2V0IGlzIHRoZSBgZ2V5c2VyYCBkYXRhIGZyYW1lIGF2YWlsYWJsZSBpbgpwYWNrYWdlIGBNQVNTYC4KCmBgYHtyfQpkYXRhKGdleXNlciwgcGFja2FnZSA9ICJNQVNTIikKZGltKGdleXNlcikKaGVhZChnZXlzZXIsIDQpCmBgYAoKPGRpdiBjbGFzcyA9ICJub3RlIj4KYGhlYWRgIGFuZCBgdGFpbGAgcmV0dXJuIHRoZSBmaXJzdCBhbmQgbGFzdCBmZXcgcm93cyBvZiBhIGRhdGEgZnJhbWUuCgpUaGV5IGFyZSB1c2VmdWwgZm9yIHF1aWNrIHNhbml0eSBjaGVja3MuCjwvZGl2PgoKVGhlIHJvd3MgcmVwcmVzZW50IG1lYXN1cmVtZW50cyByZWNvcmRlZCBmb3IgZXJ1cHRpb25zIG9mIHRoZSBbX09sZApGYWl0aGZ1bF9dKGh0dHBzOi8vd3d3LnllbGxvd3N0b25lcGFyay5jb20vdGhpbmdzLXRvLWRvL2dleXNlcnMtaG90LXNwcmluZ3MvYWJvdXQtb2xkLWZhaXRoZnVsLykKZ2V5c2VyIGluIFllbGxvd3N0b25lIE5hdGlvbmFsIFBhcmssIFd5b21pbmcuCgpUaGUgdmFyaWFibGVzIGFyZToKCiogYHdhaXRpbmdgOiB0aGUgdGltZSBpbiBtaW51dGVzIHNpbmNlIHRoZSBwcmV2aW91cyBlcnVwdGlvbjsKCiogYGR1cmF0aW9uYDogdGhlIGR1cmF0aW9uIGluIG1pbnV0ZXMgb2YgdGhlIGVydXB0aW9uLgoKVGhlIGR1cmF0aW9ucyBoYXZlIGEgYmltb2RhbCBkaXN0cmlidXRpb246CgpgYGB7ciBnZXlzZXItaGlzdCwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoZ2V5c2VyKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGR1cmF0aW9uKSwKICAgICAgICAgICAgICAgICAgIGJpbnMgPSAxNSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIpCmBgYApgYGB7ciBnZXlzZXItaGlzdCwgZXZhbCA9IEZBTFNFfQpgYGAKClRoaXMgaXMgW2BnZ3Bsb3RgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pIGNvZGUgZm9yIGNyZWF0aW5nIGEKaGlzdG9ncmFtLgoKPCEtLSAjIG5vbGludCBzdGFydCAtLT4KPGRpdiBjbGFzcyA9ICJub3RlIj4KQSBiYXNpYyB0ZW1wbGF0ZSBmb3IgY3JlYXRpbmcgYSBwbG90IHdpdGggYGdncGxvdGA6CgoKYGBgcgpnZ3Bsb3QoZGF0YSA9IDxEQVRBPikgKwogICAgPEdFT00+KG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPikpCmBgYAoKPC9kaXY+CjwhLS0gIyBub2xpbnQgZW5kIC0tPgoKQW4gaW50ZXJlc3RpbmcgcXVlc3Rpb24gaXMgd2hldGhlciB0aGUgZHVyYXRpb24gY2FuIGJlIHVzZWQgdG8gcHJlZGljdAp3aGVuIHRoZSBfbmV4dF8gZXJ1cHRpb24gd2lsbCBvY2N1ci4KCkEgcGxvdCBvZiB0aGUgX3ByZXZpb3VzXyBkdXJhdGlvbiBhZ2FpbnN0IHRoZSB3YWl0aW5nIHRpbWUgdG8gdGhlCmN1cnJlbnQgZXJ1cHRpb246CgpgYGB7ciBnZXlzZXItc2NhdHRlciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoZ2V5c2VyKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gbGFnKGR1cmF0aW9uKSwKICAgICAgICAgICAgICAgICAgIHkgPSB3YWl0aW5nKSkKYGBgCgpgYGB7ciBnZXlzZXItc2NhdHRlciwgZXZhbCA9IEZBTFNFfQpgYGAKCkl0IGxvb2tzIGxpa2UgYSB1c2VmdWwgcnVsZSB3b3VsZCBiZSB0byBleHBlY3QgYSBzaG9ydGVyIHdhaXRpbmcgdGltZQphZnRlciBhIHNob3J0ZXIgZXJ1cHRpb24gZHVyYXRpb24uCgpBbiBpbnRlcmVzdGluZyBmZWF0dXJlOgoKTWFueSBkdXJhdGlvbnMgYXJlIHJlY29yZGVkIGFzIDIgb3IgNCBtaW51dGVzLgoKVGhpcyBjYW4gYWxzbyBiZSBzZWVuIGluIGEgaGlzdG9ncmFtIHdpdGggYSBzbWFsbCBiaW4gd2lkdGg6CgpgYGB7ciBnZXlzZXItaGlzdC1uYXJyb3csIGVjaG8gPSBGQUxTRX0KcCA8LSBnZ3Bsb3QoZ2V5c2VyKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGR1cmF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgIHkgPSBzdGF0KGRlbnNpdHkpKSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMSkKcApgYGAKYGBge3IgZ2V5c2VyLWhpc3QtbmFycm93LCBldmFsID0gRkFMU0V9CmBgYAoKPGRpdiBjbGFzcyA9ICJub3RlIj4gCmBnZ3Bsb3RgIHByb2R1Y2VzIGEgcGxvdCBvYmplY3QuCgpEcmF3aW5nIG9ubHkgaGFwcGVucyB3aGVuIHRoZSBvYmplY3QgaXMgcHJpbnRlZC4KPC9kaXY+CgpEb2VzIHRoaXMgcm91bmRpbmcgbWF0dGVyPwoKKiBGb3IgbWFueSBhbmFseXNlcyBpdCBwcm9iYWJseSBkb2Vzbid0LgoKKiBJdCBtaWdodCBpZiB5b3Ugd2FudGVkIHRvIGZpdCBub3JtYWwgZGlzdHJpYnV0aW9ucyB0byB0aGUgdHdvIGdyb3Vwcy4KClRha2luZyAzIG1pbnV0ZXMgYXMgdGhlIGRpdmlkZSBiZXR3ZWVuIHNob3J0IGFuZCBsb25nIGR1cmF0aW9ucyB3ZSBjYW4KZmlyc3QgcGljayBvdXQgdGhlIHNob3J0IGFuZCBsb25nIGR1cmF0aW9uczoKCmBgYHtyfQpkIDwtIGdleXNlciRkdXJhdGlvbgpkX3Nob3J0IDwtIGRbZCA8IDNdCmRfbG9uZyA8LSBkW2QgPj0gM10KYGBgCgpUaGVuIGNvbXB1dGUgdGhlIG1lYW5zIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zIGFzCgpgYGB7cn0KbWVhbihkX3Nob3J0KQpzZChkX3Nob3J0KQptZWFuKGRfbG9uZykKc2QoZF9sb25nKQptZWFuKGQgPj0gMykKYGBgCgpBbiBhcHByb2FjaCB0aGF0IHNjYWxlcyBiZXR0ZXI6CgpDb21wdXRlIGdyb3VwIHN1bW1hcmllcyB1c2luZyB0b29scyBmcm9tIHRoZSBgZHBseXJgIHRpZHl2ZXJzZQpwYWNrYWdlLgoKRmlyc3QsIGFkZCBhIGB0eXBlYCB2YXJpYWJsZToKCmBgYHtyfQpnZXlzZXIgPC0gbXV0YXRlKGdleXNlciwgdHlwZSA9IGlmZWxzZShkdXJhdGlvbiA8IDMsICJzaG9ydCIsICJsb25nIikpCmBgYAoKVGhlIHN1bW1hcmllcyBjYW4gdGhlbiBiZSBjb21wdXRlZCBhcwoKYGBge3J9CnNnZCA8LSBzdW1tYXJpemUoZ3JvdXBfYnkoZ2V5c2VyLCB0eXBlKSwKICAgICAgICAgICAgICAgICBtZWFuID0gbWVhbihkdXJhdGlvbiksCiAgICAgICAgICAgICAgICAgc2QgPSBzZChkdXJhdGlvbiksCiAgICAgICAgICAgICAgICAgbiA9IG4oKSkKKHNnZCA8LSBtdXRhdGUoc2dkLCBwcm9wID0gbiAvIHN1bShuKSkpCmBgYAoKPGRpdiBjbGFzcyA9ICJub3RlIj4KVGhlIGZ1bmN0aW9ucyBgc3VtbWFyaXplYCwgYGdyb3VwX2J5YCwgYW5kIGBtdXRhdGVgIGFyZSBmcm9tIHRoZQpgZHBseXJgIHBhY2thZ2UgdGhhdCBpbXBsZW1lbnRzIGEgX2dyYW1tYXIgb2YgZGF0YSBtYW5pcHVsYXRpb25fLgo8L2Rpdj4KClRoaXMgY29tcHV0YXRpb24gY2FuIGFsc28gYmUgd3JpdHRlbiB1c2luZyB0aGUgX2ZvcndhcmQgcGlwZSBvcGVyYXRvcl8gYCU+JWA6CgpgYGB7cn0Kc2dkIDwtCiAgICBncm91cF9ieShnZXlzZXIsIHR5cGUpICU+JQogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKGR1cmF0aW9uKSwKICAgICAgICAgICAgICBzZCA9IHNkKGR1cmF0aW9uKSwKICAgICAgICAgICAgICBuID0gbigpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkKc2dkCmBgYAoKPGRpdiBjbGFzcyA9ICJub3RlIj4KVGhlIHBpcGUgb3BlcmF0b3IgYWxsb3dzIGEgc2VxdWVuY2Ugb2Ygb3BlcmF0aW9ucyB0byBiZSBjaGFpbmVkIHRvZ2V0aGVyLgoKVGhlIGxlZnQtaGFuZCBvcGVyYXRpb24gaXMgcGFzc2VkIGltcGxpY2l0bHkgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IHRvCnRoZSBmdW5jdGlvbiBjYWxsZWQgb24gdGhlIHJpZ2h0Lgo8L2Rpdj4KCk9uZSB3YXkgdG8gc2hvdyB0aGUgc3VwZXJpbXBvc2VkIG5vcm1hbCBkZW5zaXRpZXM6CgpgYGB7ciBnZXlzZXItaGlzdC1kZW5zLCBlY2hvID0gRkFMU0V9CmYxIDwtIGZ1bmN0aW9uKHgpCiAgICBzZ2QkcHJvcFsxXSAqIGRub3JtKHgsIHNnZCRtZWFuWzFdLCBzZ2Qkc2RbMV0pCmYyIDwtIGZ1bmN0aW9uKHgpCiAgICBzZ2QkcHJvcFsyXSAqIGRub3JtKHgsIHNnZCRtZWFuWzJdLCBzZ2Qkc2RbMl0pCnAgPC0gcCArCiAgICBzdGF0X2Z1bmN0aW9uKGNvbG9yID0gInJlZCIsIGZ1biA9IGYxKSArCiAgICBzdGF0X2Z1bmN0aW9uKGNvbG9yID0gImJsdWUiLCBmdW4gPSBmMikKcApgYGAKYGBge3IgZ2V5c2VyLWhpc3QtZGVucywgZXZhbCA9IEZBTFNFfQpgYGAKCjxkaXYgY2xhc3MgPSAibm90ZSI+IApBIGBnZ3Bsb3RgIGNhbiBjb25zaXN0IG9mIHNldmVyYWwgX2xheWVyc18uCjwvZGl2PgoKVGhlIG1lYW5zIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zIGFyZSBhZmZlY3RlZCBieSB0aGUgcm91bmRpbmcuCgpTdW1tYXJpZXMgdGhhdCBvbWl0IHZhbHVlcyBlcXVhbCB0byAyIG9yIDQgbWludXRlcyBjYW4gYmUgY29tcHV0ZWQgYXMKCmBgYHtyfQpnZXlzZXIyIDwtIGZpbHRlcihnZXlzZXIsIGR1cmF0aW9uICE9IDIsIGR1cmF0aW9uICE9IDQpCnNnZDIgPC0KICAgIGdyb3VwX2J5KGdleXNlcjIsIHR5cGUpICU+JQogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKGR1cmF0aW9uKSwKICAgICAgICAgICAgICBzZCA9IHNkKGR1cmF0aW9uKSwKICAgICAgICAgICAgICBuID0gbigpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkKc2dkMgpgYGAKCkEgcGxvdCBzaG93aW5nIGN1cnZlcyBjb21wdXRlZCBib3RoIHdheXM6CgpgYGB7ciBnZXlzZXItaGlzdC1kZW5zLTIsIGVjaG8gPSBGQUxTRX0KZjFfMiA8LSBmdW5jdGlvbih4KQogICAgc2dkMiRwcm9wWzFdICogZG5vcm0oeCwgc2dkMiRtZWFuWzFdLCBzZ2QyJHNkWzFdKQpmMl8yIDwtIGZ1bmN0aW9uKHgpCiAgICBzZ2QyJHByb3BbMl0gKiBkbm9ybSh4LCBzZ2QyJG1lYW5bMl0sIHNnZDIkc2RbMl0pCnAgPC0gcCArCiAgICBzdGF0X2Z1bmN0aW9uKGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgICAgZnVuID0gZjFfMikgKwogICAgc3RhdF9mdW5jdGlvbihjb2xvciA9ICJibHVlIiwKICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgICBmdW4gPSBmMl8yKQpwCmBgYApgYGB7ciBnZXlzZXItaGlzdC1kZW5zLTIsIGV2YWwgPSBGQUxTRX0KYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFLCBlY2hvID0gRkFMU0V9CiMjIEZhbmNpZXIgdmVyc2lvbiB0aGF0IGdldHMgYSBjb2xvciBsZWdlbmQuCiMjIENvdWxkIGFsc28gZ2V0IGEgbGluZSB0eXBlIGxlZ2VuZC4KcCA8LSBnZ3Bsb3QoZ2V5c2VyKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGR1cmF0aW9uLCB5ID0gc3RhdChkZW5zaXR5KSksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gImdyZXkiLCBjb2xvciA9ICJibGFjayIsIGJpbnMgPSA1MCkKcCA8LSBwICsgCiAgICBzdGF0X2Z1bmN0aW9uKGFlcyhjb2xvciA9IHR5cGUpLAogICAgICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKHNnZCwgdHlwZSA9PSAibG9uZyIpLAogICAgICAgICAgICAgICAgICBmdW4gPSBmdW5jdGlvbih4KQogICAgICAgICAgICAgICAgICAgICAgICAgIHNnZCRwcm9wWzFdICogZG5vcm0oeCwgc2dkJG1lYW5bMV0sIHNnZCRzZFsxXSkpICsKICAgIHN0YXRfZnVuY3Rpb24oYWVzKGNvbG9yID0gdHlwZSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoc2dkLCB0eXBlID09ICJzaG9ydCIpLAogICAgICAgICAgICAgICAgICBmdW4gPSBmdW5jdGlvbih4KQogICAgICAgICAgICAgICAgICAgICAgICAgIHNnZCRwcm9wWzJdICogZG5vcm0oeCwgc2dkJG1lYW5bMl0sIHNnZCRzZFsyXSkpCnAKCnAgPC0gcCArCiAgICAgc3RhdF9mdW5jdGlvbihhZXMoY29sb3IgPSB0eXBlKSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihzZ2QyLCB0eXBlID09ICJsb25nIiksCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgICAgZnVuID0gZnVuY3Rpb24oeCkKICAgICAgICAgICAgICAgICAgICAgICAgICBzZ2QyJHByb3BbMV0gKiBkbm9ybSh4LCBzZ2QyJG1lYW5bMV0sIHNnZDIkc2RbMV0pKSArCiAgICBzdGF0X2Z1bmN0aW9uKGFlcyhjb2xvciA9IHR5cGUpLAogICAgICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKHNnZDIsIHR5cGUgPT0gInNob3J0IiksCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgICAgZnVuID0gZnVuY3Rpb24oeCkKICAgICAgICAgICAgICAgICAgICAgICAgICBzZ2QyJHByb3BbMl0gKiBkbm9ybSh4LCBzZ2QyJG1lYW5bMl0sIHNnZDIkc2RbMl0pKQpwCmBgYAoKCiMjIyBNaW5uZXNvdGEgQmFybGV5IFlpZWxkcwoKQW5vdGhlciBjbGFzc2ljIGRhdGEgc2V0OgoKVG90YWwgeWllbGQgaW4gYnVzaGVscyBwZXIgYWNyZSBmb3IgMTAgdmFyaWV0aWVzIGF0IDYgc2l0ZXMgaW4KTWlubmVzb3RhIGluIGVhY2ggb2YgdHdvIHllYXJzLCAxOTMxIGFuZCAxOTMyLgoKVGhlIHJhdyBkYXRhOgoKYGBge3J9CmRhdGEoYmFybGV5LCBwYWNrYWdlID0gImxhdHRpY2UiKQpoZWFkKGJhcmxleSkKYGBgCgpTb21lIGluaXRpYWwgcGxvdHM6CgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnAxIDwtIGdncGxvdChiYXJsZXkpICsgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLCB5ID0gdmFyaWV0eSkpCnAyIDwtIGdncGxvdChiYXJsZXkpICsgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLCB5ID0gc2l0ZSkpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIpCmBgYAoKVXNpbmcgY29sb3IgdG8gc2VwYXJhdGUgeWllbGRzIGluIHRoZSB0d28geWVhcnM6CgpgYGB7ciwgZmlnLndpZHRoID0gMTJ9CnAxIDwtIGdncGxvdChiYXJsZXkpICsgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLCB5ID0gdmFyaWV0eSwgY29sb3IgPSB5ZWFyKSkKcDIgPC0gZ2dwbG90KGJhcmxleSkgKyBnZW9tX3BvaW50KGFlcyh4ID0geWllbGQsIHkgPSBzaXRlLCBjb2xvciA9IHllYXIpKQpjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyKQoKYGBgCgpDYW4gd2UgYWxzbyBzaG93IGBzaXRlYCB1c2luZyBzeW1ib2wgc2hhcGU/CgpgYGB7ciBiYXJsZXktY29sb3Itc3ltLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDd9CmdncGxvdChiYXJsZXkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSB5aWVsZCwKICAgICAgICAgICAgICAgICAgIHkgPSB2YXJpZXR5LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyLAogICAgICAgICAgICAgICAgICAgc2hhcGUgPSBzaXRlKSkKYGBgCmBgYHtyIGJhcmxleS1jb2xvci1zeW0sIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGVyZSBpcyBhIGxvdCBvZiBfaW50ZXJmZXJlbmNlXyBiZXR3ZWVuIHNoYXBlIGFuZCBjb2xvci4KCkxhcmdlciBwb2ludHMgbWF5IGhlbHA6CgpgYGB7ciBiYXJsZXktY29sb3Itc3ltLTIsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gN30KZ2dwbG90KGJhcmxleSkgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLAogICAgICAgICAgICAgICAgICAgeSA9IHZhcmlldHksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHllYXIsCiAgICAgICAgICAgICAgICAgICBzaGFwZSA9IHNpdGUpLAogICAgICAgICAgICAgICBzaXplID0gMi41KQpgYGAKYGBge3IgYmFybGV5LWNvbG9yLXN5bS0yLCBldmFsID0gRkFMU0V9CmBgYAoKX0ppdHRlcmluZ18gbWF5IGFsc28gaGVscDoKCgpgYGB7ciBiYXJsZXktY29sb3Itc3ltLTMsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gN30KZ2dwbG90KGJhcmxleSkgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IHlpZWxkLAogICAgICAgICAgICAgICAgICAgeSA9IHZhcmlldHksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHllYXIsCiAgICAgICAgICAgICAgICAgICBzaGFwZSA9IHNpdGUpLAogICAgICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9CiAgICAgICAgICAgICAgICAgICBwb3NpdGlvbl9qaXR0ZXIoCiAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gMC4xNSwKICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDApKQpgYGAKYGBge3IgYmFybGV5LWNvbG9yLXN5bS0zLCBldmFsID0gRkFMU0V9CmBgYAoKQW5vdGhlciBhcHByb2FjaDogX2ZhY2V0aW5nXyB0byBwcm9kdWNlIF9zbWFsbCBtdWx0aXBsZXNfLgoKYGBge3IgYmFybGV5LWZhY2V0LCBldmFsID0gRkFMU0V9CmdncGxvdChiYXJsZXkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSB5aWVsZCwKICAgICAgICAgICAgICAgICAgIHkgPSB2YXJpZXR5LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyKSkgKwogICAgZmFjZXRfd3JhcCh+c2l0ZSwgbmNvbCA9IDIpCmBgYApgYGB7ciBiYXJsZXktZmFjZXQsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDd9CmBgYAoKRm9jdXNpbmcgb24gc3VtbWFyaWVzIGNhbiBoZWxwLgoKQSBfZG90IHBsb3RfIG9mIGF2ZXJhZ2UgeWllbGRzIGZvciBlYWNoIHNpdGUgYW5kIHllYXI6CgpgYGB7ciBiYXJsZXktYXZnLWRvdCwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDd9CmJhcmxleV9zaXRlX3llYXIgPC0KICAgIGdyb3VwX2J5KGJhcmxleSwgc2l0ZSwgeWVhcikgJT4lCiAgICBzdW1tYXJpemUoeWllbGQgPSBtZWFuKHlpZWxkKSkgJT4lCiAgICB1bmdyb3VwKCkKCmdncGxvdChiYXJsZXlfc2l0ZV95ZWFyKSArCiAgICBnZW9tX3BvaW50KGFlcyh5ID0gc2l0ZSwKICAgICAgICAgICAgICAgICAgIHggPSB5aWVsZCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0geWVhciksCiAgICAgICAgICAgICAgIHNpemUgPSAzKQpgYGAKYGBge3IgYmFybGV5LWF2Zy1kb3QsIGV2YWwgPSBGQUxTRX0KYGBgCgpBZGRpbmcgbGluZXMgY2FuIGhlbHAgY29tcGFyaW5nIHRoZSBjaGFuZ2VzLiBUaGlzIGlzIHNvbWV0aW1lcyBjYWxsZWQKYSBfZHVtYmJlbGwgY2hhcnRfOgoKCmBgYHtyIGJhcmxleS1hdmctZG90LTIsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA3fQpiYXJsZXlfc2l0ZV95ZWFyIDwtCiAgICBncm91cF9ieShiYXJsZXksIHNpdGUsIHllYXIpICU+JQogICAgc3VtbWFyaXplKHlpZWxkID0gbWVhbih5aWVsZCkpICU+JQogICAgdW5ncm91cCgpCgpnZ3Bsb3QoYmFybGV5X3NpdGVfeWVhcikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gc2l0ZSwKICAgICAgICAgICAgICAgICAgeCA9IHlpZWxkLAogICAgICAgICAgICAgICAgICBncm91cCA9IHNpdGUpLAogICAgICAgICAgICAgIGNvbG9yID0gImRhcmtncmV5IiwKICAgICAgICAgICAgICBzaXplID0gMikgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IHNpdGUsCiAgICAgICAgICAgICAgICAgICB4ID0geWllbGQsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHllYXIpLAogICAgICAgICAgICAgICBzaXplID0gNCkKYGBgCmBgYHtyIGJhcmxleS1hdmctZG90LTIsIGV2YWwgPSBGQUxTRX0KYGBgCgpBbm90aGVyIHVzZWZ1bCBhcHByb2FjaCBmb3Igc2hvd2luZyByZXBlYXRlZCBtZWFzdXJlbWVudHMgaXMgYSBfc2xvcGUKZ3JhcGhfOgoKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KbGlicmFyeShnZ3JlcGVsKQpiYXJsZXlfc2l0ZV95ZWFyIDwtCiAgICBtdXRhdGUoYmFybGV5X3NpdGVfeWVhciwgeWVhciA9IGZjdF9yZXYoeWVhcikpCmJhcmxleV9zaXRlX3llYXJfMTkzMiA8LQogICAgZmlsdGVyKGJhcmxleV9zaXRlX3llYXIsIHllYXIgPT0gIjE5MzIiKQpnZ3Bsb3QoYmFybGV5X3NpdGVfeWVhciwKICAgICAgIGFlcyh4ID0geWVhciwgeSA9IHlpZWxkLCBncm91cCA9IHNpdGUpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gc2l0ZSksCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhcmxleV9zaXRlX3llYXJfMTkzMiwKICAgICAgICAgICAgICAgICAgICBoanVzdCA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAieSIpICsKICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMSwgLjI1KSksCiAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gInRvcCIpICsKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSAiQXZlcmFnZSBZaWVsZCIpCmBgYAoKVGhpcyBlbXBoYXNpemVzIHRoZSByZXZlcnNhbCBmb3IgTW9ycmlzLgoKX0JhciBjaGFydHNfIGFyZSBzb21ldGltZXMgdXNlZCBmb3Igc3VtbWFyaWVzLCBidXQgZG90IHBsb3RzIGFyZQp1c3VhbGx5IGEgYmV0dGVyIGNob2ljZS4KCmBgYHtyIGJhcmxleS1hdmctYmFyLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gN30KZ2dwbG90KGJhcmxleV9zaXRlX3llYXIpICsKICAgIGdlb21fY29sKGFlcyh4ID0geWllbGQsCiAgICAgICAgICAgICAgICAgeSA9IHNpdGUsCiAgICAgICAgICAgICAgICAgZmlsbCA9IHllYXIpLAogICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgICBwb3NpdGlvbiA9ICJkb2RnZSIsCiAgICAgICAgICAgICB3aWR0aCA9IC40KQpgYGAKYGBge3IgYmFybGV5LWF2Zy1iYXIsIGV2YWwgPSBGQUxTRX0KYGBgCgojIyMgQmFyIENoYXJ0cyBhbmQgdGhlIFplcm8gQmFzZSBMaW5lCgpCZWNhdXNlIG9mIHRoZSB3YXkgd2UgcGVyY2VpdmUgYmFycywgaXQgaXMgaW1wb3J0YW50IHRvIHVzZSBhIFt6ZXJvCmJhc2UgbGluZSBmb3IgYmFyCmNoYXJ0c10oaHR0cHM6Ly9mbG93aW5nZGF0YS5jb20vMjAxNS8wOC8zMS9iYXItY2hhcnQtYmFzZWxpbmVzLXN0YXJ0LWF0LXplcm8vKS4KCiFbXShgciBoZXJlX3JlbCgiaW1nL3ZpejMtNTIweDI5NC5qcGciKWApCiFbXShgciBoZXJlX3JlbCgiaW1nL3ZpejUtNTIweDI4MC5qcGciKWApCgoKIyMjIEhhaXIgYW5kIEV5ZSBDb2xvciBEYXRhCgpBIGRhdGEgc2V0IHJlY29yZGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGhhaXIgYW5kIGV5ZSBjb2xvciBhbmQgc2V4IGluCjU5MiBzdGF0aXN0aWNzIHN0dWRlbnRzLgoKYGBge3J9CkhhaXJFeWVERiA8LSBhcy5kYXRhLmZyYW1lKEhhaXJFeWVDb2xvcikKaGVhZChIYWlyRXllREYpCmBgYAoKVGhlIGRhdGEgc2V0IGlzIGF2YWlsYWJsZSBhcyBhIF9jcm9zcy10YWJ1bGF0aW9uXy4KCmBhcy5kYXRhLmZyYW1lYCBjb252ZXJ0cyBpdCB0byBhIGRhdGEgZnJhbWUuCgpMb29raW5nIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZXllIGNvbG9yOgoKYGBge3IgZXllLWJhciwgZWNobyA9IEZBTFNFfQpleWUgPC0KICAgIGdyb3VwX2J5KEhhaXJFeWVERiwgRXllKSAlPiUKICAgIHN1bW1hcml6ZShGcmVxID0gc3VtKEZyZXEpKSAlPiUKICAgIHVuZ3JvdXAoKQoKZ2dwbG90KGV5ZSkgKwogICAgZ2VvbV9jb2woYWVzKHggPSBFeWUsCiAgICAgICAgICAgICAgICAgeSA9IEZyZXEpLAogICAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKYGBge3IgZXllLWJhciwgZXZhbCA9IEZBTFNFfQpgYGAKCk1hcHBpbmcgZXllIGNvbG9yIHRvIGJhciBjb2xvciBpbiBhZGRpdGlvbiB0byB0aGUgaG9yaXpvbnRhbCBheGlzCnBvc2l0aW9uIGNhbiBoZWxwOgoKYGBge3IgZXllLWJhci0yLCBlY2hvID0gRkFMU0V9CmdncGxvdChleWUpICsKICAgIGdlb21fY29sKGFlcyh4ID0gRXllLAogICAgICAgICAgICAgICAgIHkgPSBGcmVxLAogICAgICAgICAgICAgICAgIGZpbGwgPSBFeWUpLAogICAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKYGBge3IgZXllLWJhci0yLCBldmFsID0gRkFMU0V9CmBgYAoKTW9yZSBzZW5zaWJsZSBjb2xvcnMgd291bGQgYmUgbmljZSBidXQgcmVxdWlyZSBhIGJpdCBvZiB3b3JrOgoKYGBge3IgZXllLWJhci0zLCBlY2hvID0gRkFMU0V9CmhhemVsX3JnYiA8LQogICAgY29sMnJnYigiYnJvd24iKSAqIDAuNzUgKyBjb2wycmdiKCJncmVlbiIpICogMC4yNQpoYXplbCA8LQogICAgZG8uY2FsbChyZ2IsIGFzLmxpc3QoaGF6ZWxfcmdiIC8gMjU1KSkKCmNvbHMgPC0KICAgIGMoQmx1ZSA9IGNvbG9yc3BhY2U6OmxpZ2h0ZW4oY29sb3JzcGFjZTo6ZGVzYXR1cmF0ZSgiYmx1ZSIsIDAuMyksIDAuMyksCiAgICAgIEdyZWVuID0gY29sb3JzcGFjZTo6bGlnaHRlbigiZm9yZXN0Z3JlZW4iLCAwLjEpLAogICAgICBCcm93biA9IGNvbG9yc3BhY2U6OmxpZ2h0ZW4oImJyb3duIiwgMC4wMDAxKSwgIyMgMC4zPwogICAgICBIYXplbCA9IGNvbG9yc3BhY2U6OmxpZ2h0ZW4oaGF6ZWwsIDAuMykpCgpwYiA8LSBnZ3Bsb3QoZXllKSArCiAgICBnZW9tX2NvbChhZXMoeCA9IEV5ZSwKICAgICAgICAgICAgICAgICB5ID0gRnJlcSwKICAgICAgICAgICAgICAgICBmaWxsID0gRXllKSwKICAgICAgICAgICAgIHBvc2l0aW9uID0gImRvZGdlIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykKcGIKYGBgCmBgYHtyIGV5ZS1iYXItMywgZXZhbCA9IEZBTFNFfQpgYGAKCkEgX3N0YWNrZWQgYmFyIGNoYXJ0XyBjYW4gYWxzbyBiZSB1c2VmdWw6CgpgYGB7ciBleWUtYmFyLXN0YWNrZWQsIGVjaG8gPSBGQUxTRX0KcHNiIDwtIGdncGxvdChleWUpICsKICAgIGdlb21fY29sKGFlcyh4ID0gIiIsIHkgPSBGcmVxLCBmaWxsID0gRXllKSwgY29sb3IgPSAibGlnaHRncmV5IikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykKcHNiCmBgYApgYGB7ciBleWUtYmFyLXN0YWNrZWQsIGV2YWwgPSBGQUxTRX0KYGBgCgpDaGFuZ2luZyB0byBwb2xhciBjb29yZGluYXRlcyBwcm9kdWNlcyBhIF9waWUgY2hhcnRfOgoKYGBge3IgZXllLXBpZSwgZWNobyA9IEZBTFNFfQoocHAgPC0gcHNiICsgY29vcmRfcG9sYXIoInkiKSkKYGBgCmBgYHtyIGV5ZS1waWUsIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGUgYXhpcyBhbmQgZ3JpZCBhcmUgbm90IGhlbHBmdWw7IGEgX3RoZW1lXyBhZGp1c3RtZW50IGNhbiByZW1vdmUgdGhlbToKCmBgYHtyIGV5ZS1waWUtMiwgZWNobyA9IEZBTFNFfQoocHAgPC0gcHAgKyB0aGVtZV92b2lkKCkpCmBgYApgYGB7ciBleWUtcGllLTIsIGV2YWwgPSBGQUxTRX0KYGBgCgo8ZGl2IGNsYXNzID0gIm5vdGUiPgpUaGVtZXMgcHJvdmlkZSBhIHdheSB0byBjdXN0b21pemUgdGhlIG5vbi1kYXRhIGNvbXBvbmVudHMgb2YgcGxvdHM6CmkuZS4gdGl0bGVzLCBsYWJlbHMsIGZvbnRzLCBiYWNrZ3JvdW5kLCBncmlkIGxpbmVzLCBhbmQgbGVnZW5kcy4KClRoZW1lcyBjYW4gYmUgdXNlZCB0byBnaXZlIHBsb3RzIGEgY29uc2lzdGVudCBjdXN0b21pemVkIGxvb2suCgpUaGUgYGdndGhlbWVzYCBwYWNrYWdlIHByb3ZpZGVzIGEgbnVtYmVyIG9mIHRoZW1lcyB0byBlbXVsYXRlIHRoZQpzdHlsZSBvZiBkaWZmZXJlbnQgcHVibGljYXRpb25zLCBmb3IgZXhhbXBsZSBgdGhlbWVfd3NqYCBhbmQKYHRoZW1lX2Vjb25vbWlzdGAuCjwvZGl2PgoKSG93IHdlbGwgZG8gYmFyIGNoYXJ0cyBhbmQgcGllIGNoYXJ0cyB3b3JrPwoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTB9CmNvd3Bsb3Q6OnBsb3RfZ3JpZChwYiwgcHApCmBgYAoKU29tZSBxdWVzdGlvbnM6CgoqIFdoaWNoIHBsb3QgbWFrZXMgaXQgZWFzaWVyIHRvIHRlbGwgd2hldGhlciB0aGUgcHJvcG9ydGlvbiBvZgogIGJyb3duLWV5ZWQgc3R1ZGVudHMgaXMgbGFyZ2VyIG9yIHNtYWxsZXIgdGhhbiB0aGUgcHJvcG9ydGlvbiBvZgogIGJsdWUtZXllZCBzdHVkZW50cz8KCiogV2hpY2ggcGxvdCBtYWtlcyBpdCBlYXNpZXIgdG8gdGVsbCB3aGV0aGVyIHRoZXNlIHByb3BvcnRpb25zIGFyZQogIGxhcmdlciBvciBzbWFsbGVyIHRoYW4gMS8yIG9yIDEvNCBvciAxLzM/CgpMb29raW5nIGF0IHRoZSBwcm9wb3J0aW9ucyB3aXRoaW4gaGFpciBjb2xvciBhbmQgc2V4OgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KZXllX2hhaXJzZXggPC0KICAgIGdyb3VwX2J5KEhhaXJFeWVERiwgSGFpciwgU2V4KSAlPiUKICAgIG11dGF0ZShQcm9wID0gRnJlcSAvIHN1bShGcmVxKSkgJT4lCiAgICB1bmdyb3VwKCkKCnAxIDwtIGdncGxvdChleWVfaGFpcnNleCkgKwogICAgZ2VvbV9jb2woYWVzKHggPSBFeWUsIHkgPSBQcm9wLCBmaWxsID0gRXllKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykgKwogICAgZmFjZXRfZ3JpZChIYWlyIH4gU2V4KQpwMiA8LSBnZ3Bsb3QoZXllX2hhaXJzZXgpICsKICAgIGdlb21fY29sKGFlcyh4ID0gIiIsIHkgPSBQcm9wLCBmaWxsID0gRXllKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykgKwogICAgY29vcmRfcG9sYXIoInkiKSArCiAgICBmYWNldF9ncmlkKEhhaXIgfiBTZXgpICsKICAgIHRoZW1lX3ZvaWQoKQpjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyKQpgYGAKCgojIyMgQSBtb3JlIGNvbXBsZXRlIGBnZ3Bsb3RgIHRlbXBsYXRlCgo8IS0tICMgbm9saW50IHN0YXJ0IC0tPgoKYGBgcgpnZ3Bsb3QoZGF0YSA9IDxEQVRBPikgKwogICAgPEdFT00+KG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPiksCiAgICAgICAgICAgc3RhdCA9IDxTVEFUPiwKICAgICAgICAgICBwb3NpdGlvbiA9IDxQT1NJVElPTj4pICsKICAgIDwgLi4uIE1PUkUgR0VPTVMgLi4uID4gKwogICAgPENPT1JESU5BVEVfQURKVVNUTUVOVD4gKwogICAgPFNDQUxFX0FESlVTVE1FTlQ+ICsKICAgIDxGQUNFVElORz4gKwogICAgPFRIRU1FX0FESlVTVE1FTlQ+CmBgYAo8IS0tICMgbm9saW50IGVuZCAtLT4KCgojIyBWaXN1YWwgUGVyY2VwdGlvbiBhbmQgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MKCiMjIyBNb250aGx5IFJpdmVyIEZsb3dzCgpNb250aGx5IGZsb3cgdm9sdW1lcyByZWNvcmRlZCBmb3IgYSByaXZlciBpbiB0aGUgcGFjaWZpYyBub3J0aC13ZXN0LgoKQW4gaW5pdGlhbCBwbG90IHVzaW5nIGRlZmF1bHQgc2V0dGluZ3M6CgpgYGB7ciwgZmlnLndpZHRoID0gOCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnJpdmVyIDwtIHNjYW4oaGVyZTo6aGVyZSgiZGF0YS9yaXZlci5kYXQiKSkKcmQgPC0gZGF0YS5mcmFtZShmbG93ID0gcml2ZXIsIG1vbnRoID0gc2VxX2Fsb25nKHJpdmVyKSkKKHBwIDwtIGdncGxvdChyZCkgKyBnZW9tX3BvaW50KGFlcyh4ID0gbW9udGgsIHkgPSBmbG93KSkpCmBgYAoKQ2hhbmdpbmcgdGhlIF9hc3BlY3QgcmF0aW9fOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcHAgKyBjb29yZF9maXhlZCgzLjUpCmBgYAoKVGltZSBzZXJpZXMgYXJlIG9mdGVuIHZpc3VhbGl6ZWQgd2l0aCBhIGxpbmUgcGxvdDoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnBsIDwtIGdncGxvdChyZCkgKyBnZW9tX2xpbmUoYWVzKHggPSBtb250aCwgeSA9IGZsb3cpKQpwbCArIGNvb3JkX2ZpeGVkKDMuNSkKYGBgCgpUaGUgc2Vhc29uYWwgdmFyaWF0aW9uIGNhbiBiZSBzZWVuIHdpdGggYSBsaW5lIHBsb3QgaW4gdGhlIG9yaWdpbmFsCmFzcGVjdCByYXRpbzoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnBsCmBgYAoKIyMjIEEgU2ltcGxlIE1vZGVsIG9mIFZpc3VhbCBQZXJjZXB0aW9uCgpUaGUgZXllcyBhY3F1aXJlIGFuIGltYWdlLCB3aGljaCBpcyBwcm9jZXNzZWQgdGhyb3VnaCB0aHJlZSBzdGFnZXMgb2YKbWVtb3J5OgoKKiBJY29uaWMgbWVtb3J5CgoqIFdvcmtpbmcgbWVtb3J5LCBvciBzaG9ydC10ZXJtIG1lbW9yeQoKKiBMb25nLXRlcm0gbWVtb3J5CgpUaGUgZmlyc3QgcHJvY2Vzc2luZyBzdGFnZSBvZiBhbiBpbWFnZSBoYXBwZW5zIGluIGljb25pYyBtZW1vcnkuCgoqIEltYWdlcyByZW1haW4gaW4gaWNvbmljIG1lbW9yeSBmb3IgbGVzcyB0aGFuIGEgc2Vjb25kLgoKKiBQcm9jZXNzaW5nIGluIGljb25pYyBtZW1vcnkgaXMgbWFzc2l2ZWx5IHBhcmFsbGVsIGFuZCBhdXRvbWF0aWMuCgoqIFRoaXMgaXMgY2FsbGVkIF9wcmVhdHRlbnRpdmUgcHJvY2Vzc2luZ18uCgpQcmVhdHRlbnRpdmUgcHJvY2Vzc2luZyBpcyBhIGZhc3QgcmVjb2duaXRpb24gcHJvY2Vzcy4KCk1lYW5pbmdmdWwgdmlzdWFsIGNodW5rcyBhcmUgbW92ZWQgZnJvbSBpY29uaWMgbWVtb3J5IHRvIHNob3J0IHRlcm0gbWVtb3J5LgoKKiBUaGVzZSBjaHVua3MgYXJlIHVzZWQgYnkgY29uc2Npb3VzLCBvciBhdHRlbnRpdmUsIHByb2Nlc3NpbmcuCgoqIEF0dGVudGl2ZSBwcm9jZXNzaW5nIG9mdGVuIGludm9sdmVzIGNvbnNjaW91cyBjb21wYXJpc29ucyBvciBzZWFyY2guCgoqIFNob3J0IHRlcm0gbWVtb3J5IGlzIGxpbWl0ZWQ7CgogICAgKiBpbmZvcm1hdGlvbiBpcyByZXRhaW5lZCBmb3Igb25seSBhIGZldyBzZWNvbmRzOwogICAgKiBvbmx5IHRocmVlIG9yIGZvdXJzIGNodW5rcyBjYW4gYmUgaGVsZCBhdCBhIHRpbWUuCgpMb25nIHRlcm0gdmlzdWFsIG1lbW9yeSBpcyBidWlsdCB1cCBvdmVyIGEgbGlmZXRpbWUsIHRob3VnaAppbmZyZXF1ZW50bHkgdXNlZCB2aXN1YWwgY2h1bmtzIG1heSBiZWNvbWUgbG9zdC4KCjxkaXYgY2xhc3MgPSAibm90ZSI+CioqVmlzdWFsIERlc2lnbiBJbXBsaWNhdGlvbnMqKgoKVHJ5IHRvIG1ha2UgYXMgbXVjaCB1c2Ugb2YgcHJlYXR0ZW50aXZlIGZlYXR1cmVzIGFzIHBvc3NpYmxlLgoKUmVjb2duaXplIHdoZW4gcHJlYXR0ZW50aXZlIGZlYXR1cmVzIG1pZ2h0IG1pc2xlYWQuCgpGb3IgZmVhdHVyZXMgdGhhdCByZXF1aXJlIGF0dGVudGl2ZSBwcm9jZXNzaW5nLCBrZWVwIGluIG1pbmQgdGhhdAp3b3JraW5nIG1lbW9yeSBpcyBsaW1pdGVkLgo8L2Rpdj4KCgojIyMgU29tZSBUZXJtcyBmb3IgRGVzY3JpYmluZyBWaXN1YWxpemF0aW9ucwoKRGF0YSB0byBiZSB2aXN1YWxpemVkIGNvbnRhaW5zIF92YXJpYWJsZXNfIG9yIF9hdHRyaWJ1dGVzXyBtZWFzdXJlZCBvbgppbmRpdmlkdWFsIF9pdGVtc18gb3IgX2Nhc2VzXy4KCl9MaW5rc18gYXJlIHJlbGF0aW9uc2hpcHMgdGhhdCBtYXkgZXhpc3QgYW1vbmcgaXRlbXMsIGUuZy4gbW9udGhzCndpdGhpbiBhIHllYXIgb3IgY291bnRyaWVzIHdpdGhpbiBhIGNvbnRpbmVudC4KCl9NYXJrc18gYXJlIGluZGl2aWR1YWwgZ2VvbWV0cmljIGVudGl0aWVzIHVzZWQgdG8gcmVwcmVzZW50IGl0ZW1zOgpwb2ludHMuIGJhcnMsIGV0Yy4KCl9BZXN0aGV0aWNzXyBvciBfdmlzdWFsIGNoYW5uZWxzXyBhcmUgdGhlIHZpc3VhbCBmZWF0dXJlcyBvZiBtYXJrcwp0aGF0IGNhbiBiZSB1c2VkIHRvIGVuY29kZSBhdHRyaWJ1dGVzLgoKVGhlIGBhZXMoLi4uKWAgZXhwcmVzc2lvbnMgZXN0YWJsaXNoIHRoZSBtYXBwaW5nIGJldHdlZW4gYXR0cmlidXRlcwphbmQgdmlzdWFsIGNoYW5uZWxzLgoKVGhlc2UgaWRlYXMgY2xvc2VseSBtaXJyb3IgdGhlIHN0cnVjdHVyZSBvZiB0aGUgX2dyYW1tYXIgb2YgZ3JhcGhpY3NfCmFzIGltcGxlbWVudGVkIGluIGBnZ3Bsb3RgLgoKPiBNdW56bmVyLCBULiAoMjAxNCksIFtfVmlzdWFsaXphdGlvbiBBbmFseXNpcyBhbmQKPiAgRGVzaWduX10oaHR0cDovL3d3dy5jcy51YmMuY2EvfnRtbS92YWRib29rLyksIENSQyBQcmVzcy4KCj4gV2lsa2luc29uLCBMLiAoMjAwNSksIF9UaGUgR3JhbW1hciBvZiBHcmFwaGljc18sIDJuZCBlZCwgU3ByaW5nZXIuCgoKIyMjIENoYW5uZWxzIGFuZCB0aGVpciBBY2N1cmFjeQoKQSB1c2VmdWwgZGlzdGluY3Rpb24gYW1vbmcgY2hhbm5lbHM6CgoqIF9NYWduaXR1ZGUgY2hhbm5lbHNfIGNhbiByZWZsZWN0IG9yZGVyIGFuZCBudW1lcmljIHZhbHVlcywKICBlLmcuIHBvc2l0aW9uIG9uIGFuIGF4aXMsIGxlbmd0aCwgYXJlYSwgYnJpZ2h0bmVzcy4KCiogX0lkZW50aXR5IGNoYW5uZWxzXyBjYW4gZGlzdGluZ3Vpc2ggZGlmZmVyZW50IHZhbHVlcyBidXQgbm90IHJlZmxlY3QKICBvcmRlciwgZS5nLiBodWUsIHNoYXBlLCBncm91cGluZy4KClNvbWUgY2hhbm5lbHMgYXJlIGJldHRlciBhdCBjb252ZXlpbmcgaW5mb3JtYXRpb24gdGhhbiBvdGhlcnMuCgpNdW56bmVyJ3Mgb3JkZXJpbmcgYnkgYWNjdXJhY3k6Cgp8IE1hZ25pdHVkZSBDaGFubmVscyAoT3JkZXJlZCwgTnVtZXJpY2FsKSB8IElkZW50aXR5IENoYW5uZWxzIChDYXRlZ29yaWNhbCkgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IFBvc2l0aW9uIG9uIGNvbW1vbiBzY2FsZSAgICAgICAgICAgICAgICB8IFNwYXRpYWwgZ3JvdXBpbmcgICAgICAgICAgICAgICAgfAp8IFBvc2l0aW9uIG9uIHVuYWxpZ25lZCBzY2FsZSAgICAgICAgICAgICB8IENvbG9yIGh1ZSAgICAgICAgICAgICAgICAgICAgICAgfAp8IExlbmd0aCAoMUQgc2l6ZSkgICAgICAgICAgICAgICAgICAgICAgICB8IFNoYXBlICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IFRpbHQsIGFuZ2xlICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEFyZWEgKDJEIHNpemUpICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IERlcHRoICgzRCBwb3NpdGlvbikgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IENvbG9yIGx1bWluYW5jZSwgc2F0dXJhdGlvbiAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEN1cnZhdHVyZSwgdm9sdW1lICgzRCBzaXplKSAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKTGluZSB3aWR0aCBpcyBhbm90aGVyIGNoYW5uZWw7IG5vdCBzdXJlIHRoZXJlIGlzIGFncmVlbWVudCBvbiBpdHMKYWNjdXJhY3ksIGJ1dCBpdCBpcyBub3QgaGlnaC4KCjxkaXYgY2xhc3MgPSAibm90ZSI+CioqVmlzdWFsIERlc2lnbiBJbXBsaWNhdGlvbnMqKgoKVHJ5IHRvIG1hcCB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIHRvIHRoZSBzdHJvbmdlc3QgY2hhbm5lbHMuCjwvZGl2PgoKCiMjIyBDb2xvcgoKQ29sb3IgaXMgdmVyeSBlZmZlY3RpdmUgd2hlbiB1c2VkIHdlbGwuCgpCdXQgdXNpbmcgY29sb3Igd2VsbCBpcyBub3QgZWFzeS4KClNvbWUgb2YgdGhlIGlzc3VlczoKCiogUGVyY2VwdGlvbiBkZXBlbmRzIG9uIGNvbnRleHQuCgoqIFNpbXBsZSBjb2xvciBhc3NpZ25tZW50cyBtYXkgbm90IHNlcGFyYXRlIGVxdWFsbHkgd2VsbC4KCiogRWZmZWN0aXZlbmVzcyBtYXkgdmFyeSB3aXRoIHRoZSBtZWRpdW0gKHNjcmVlbiwgcHJvamVjdG9yLCBwcmludCkuCgoqIFNvbWUgcGVvcGxlIGRvIG5vdCBwZXJjZWl2ZSB0aGUgZnVsbCBzcGVjdHVybSBvZiBjb2xvcnMuCgoqIEdyZXkgc2NhbGUgcHJpbnRpbmcuCgoqIFNvbWUgY29sb3JzIGhhdmUgY3VsdHVyYWwgc2lnbmlmaWNhbmNlLgoKKiBDdWx0dXJhbCBzaWduaWZpY2FuY2UgbWF5IHZhcnkgYW1vbmcgY3VsdHVyZXMgYW5kIHdpdGggdGltZS4KCkNvbG9yIHBlcmNlcHRpb24gaXMgcmVsYXRpdmU6CgohW10oYHIgaGVyZV9yZWwoImltZy9jaGVzczEucG5nIilgKQohW10oYHIgaGVyZV9yZWwoImltZy9jaGVzczIucG5nIilgKQoKR3JvdXBzIG9mIGNvbG9ycyB0aGF0IHdvcmsgd2VsbCB0b2dldGhlciBhcmUgY2FsbGVkIF9wYWxldHRlc18uCgpTb21lIHRvb2xzIGZvciBzZWxlY3RpbmcgcGFsZXR0ZXMgaW5jbHVkZToKCiogW0NvbG9yQnJld2VyXShodHRwOi8vY29sb3JicmV3ZXIyLm9yZyk7IGF2YWlsYWJsZSBpbiB0aGUKICBgUkNvbG9yQnJld2VyYCBwYWNrYWdlLgoKKiBbSENMIFdpemFyZF0oaHR0cDovL3d3dy5oY2x3aXphcmQub3JnLyk7IGFsc28gYXZhaWxhYmxlIGFzIGBoY2x3aXphcmRgCiAgaW4gdGhlIGBjb2xvcnNwYWNlYCBwYWNrYWdlLgoKQSBub3RlIG9uIFtyYWluYm93IGNvbG9yc10oCmh0dHBzOi8vZWVlY29uLnVpYmsuYWMuYXQvfnplaWxlaXMvbmV3cy9lbmRyYWluYm93LykuCgoKIyMgQSBHcmFtbWFyIG9mIERhdGEgTWFuaXB1bGF0aW9uCgpUaGUgYGRwbHlyYCBwYWNrYWdlIHByb3ZpZGVzIGEgbGFuZ3VhZ2UsIG9yIGdyYW1tYXIsIGZvciBkYXRhCm1hbmlwdWxhdGlvbi4KClRoZSBkZXNpZ24gb2YgYGRwbHlyYCBpcyBzdHJvbmdseSBtb3RpdmF0ZWQgYnkgU1FMLgoKVGhlIGxhbmd1YWdlIGNvbnRhaW5zIGEgbnVtYmVyIG9mIF92ZXJic18gdGhhdCBvcGVyYXRlIG9uIHRhYmxlcy4KClRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgdmVyYnMgb3BlcmF0ZSBvbiBhIHNpbmdsZSBkYXRhIGZyYW1lOgoKKiBgc2VsZWN0YDogcGljayB2YXJpYWJsZXMgYnkgdGhlaXIgbmFtZXMKCiogYGZpbHRlcmA6IGNob29zZSByb3dzIHRoYXQgc2F0aXNmeSBzb21lIGNyaXRlcmlhCgoqIGBtdXRhdGVgOiBjcmVhdGUgdHJhbnNmb3JtZWQgb3IgZGVyaXZlZCB2YXJpYWJsZXMKCiogYGFycmFuZ2VgOiByZW9yZGVyIHRoZSByb3dzCgoqIGBzdW1tYXJpemVgOiBjb2xsYXBzZSByb3dzIGRvd24gdG8gc3VtbWFyaWVzCgpUaGVyZSBhcmUgYWxzbyBhIG51bWJlciBvZiBgam9pbmAgdmVyYnMgdGhhdCBtZXJnZSBzZXZlcmFsIGRhdGEgZnJhbWVzCmludG8gb25lLgoKUGFja2FnZSBgdGlkeXJgIHByb3ZpZGVzIG1vcmUgdmVyYnMsIHN1Y2ggYXMgYHBpdm90X2xvbmdlcmAgYW5kCmBwaXZvdF93aWRlcmAgZm9yIHJlc2hhcGluZyBkYXRhIGZyYW1lcy4KClRoZSBzaW5nbGUgdGFibGUgdmVyYnMgY2FuIGFsc28gYmUgdXNlZCB3aXRoIGBncm91cF9ieWAgdG8gd29yawpzZXBhcmF0ZWx5IG9uIGdyb3VwcyBvZiByb3dzLgoKCiMjIE1vcmUgRXhhbXBsZXMKClRoZXNlIGV4YW1wbGVzIHN0YXJ0IHdpdGggcmF3IGRhdGEgYXMgeW91IG1pZ2h0IHJlY2VpdmUgaXQgZnJvbSBhCnJlc2VhcmNoZXIsIGFuZCBpbnZvbHZlIHJlYWRpbmcgYW5kIGNsZWFuaW5nIHRoZSBkYXRhLgoKQ29tbW9uIGRhdGEgZm9ybWF0cyB5b3UgbWlnaHQgZW5jb3VudGVyIGluY2x1ZGUKCiogW19DU1ZfIChjb21tYS1zZXBhcmF0ZWQKICB2YWx1ZXMpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db21tYS1zZXBhcmF0ZWRfdmFsdWVzKSBmaWxlcy4KCiogVGV4dCBmaWxlcyB1c2luZyBvdGhlciBkZWxpbWl0ZXJzLCBzdWNoIGFzIHRhYnMgb3IgYHxgIGNoYXJhY3RlcnMuCgoqIFtKU09OKEphdmFTY3JpcHQgT2JqZWN0CiAgTm90YXRpb24pXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9KU09OKSBmaWxlcy4KICAKCiogW1hNTCAoRXh0ZW5zaWJsZSBNYXJrdXAKICBMYW5ndWFnZSldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1hNTCkgZmlsZXMuCgoqIEV4Y2VsIHNwcmVhZHNoZWV0cy4KClRvb2xzIGFyZSBhdmFpbGFibGUgZm9yIHJlYWRpbmcgZGF0YSBpbiB0aGVzZSBmb3JtYXRzIGludG8gUi4KCiMjIyBXaW5kIFR1cmJpbmVzIGluIElvd2EKClRoZXJlIGFyZSBtYW55IHdpbmQgdHVyYmluZXMgaW4gSW93YS4KCkRhdGEgaXMgYXZhaWxhYmxlIGZyb20gdGhlIFtVLlMuIFdpbmQgVHVyYmluZQpEYXRhYmFzZV0oaHR0cHM6Ly9lZXJzY21hcC51c2dzLmdvdi91c3d0ZGIvKS4KCkEgc25hcHNob3QgaXMgYXZhaWxhYmxlIGlzIFtoZXJlXShgciBoZXJlX3JlbCgiZGF0YS91c193aW5kLmNzdiIpYCkKYXMgYSBDU1YgZmlsZS4KCiogQ1NWIGZpbGVzIGFyZSBhIGNvbW1vbiBmb3JtIG9mIGRhdGEgZXhjaGFuZ2UuCgoqIFRoZXkgYXJlIHNpbXBsZSB0ZXh0IGZpbGVzIHRoYXQgYXJlIGludGVuZGVkIHRvIGJlIHdyaXR0ZW4gYW5kIHJlYWQKICBieSBhIGNvbXB1dGVyLgoKKiBTb21lIENTViBmaWxlcyBpbmNsdWRlIGEgaGVhZGVyIGFuZCBhIGZvb3RlciB0aGF0IG5lZWQgdG8gaGUgaGFuZGxlZC4KCiogT25lIGlzc3VlIGlzIHRoYXQgYSBjb21tYSBpc24ndCBhIGdvb2Qgc2VwYXJhdG9yIGluIGNvdW50cmllcyB3aGVyZQogIGl0IGlzIHRoZSBkZWNpbWFsIHNlcGFyYXRvciEKCiogQSBDU1YgZmlsZSBjYW4gYmUgcmVhZCB1c2luZyBgcmVhZC5jc3ZgIG9yIGByZWFkcjo6cmVhZF9jc3ZgLgoKUmVhZGluZyB0aGUgd2luZCB0dXJiaW5lIGRhdGE6CgpgYGB7cn0Kd2luZF90dXJiaW5lcyA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJkYXRhL3VzX3dpbmQuY3N2IiksIGNvbW1lbnQgPSAiIyIpCmBgYApTb21lIGRhdGEgY2xlYW5pbmcgaXMgbmVlZGVkLgoKRm9jdXMgb24gdGhlIHdpbmQgdHVyYmluZXMgaW4gSU9XQSAoMTkgaXMgdGhlIHN0YXRlIGNvbXBvbmVudCBvZiB0aGUKW0ZJUFMgY291bnR5IGNvZGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZJUFNfY291bnR5X2NvZGUpIGZvcgpJb3dhKToKCmBgYHtyfQp3dF9JQSA8LSBmaWx0ZXIod2luZF90dXJiaW5lcywgdF9maXBzICUvJSAxMDAwID09IDE5KQpgYGAKCkRyb3AgZW50cmllcyB3aXRoIG1pc3NpbmcgbG9uZ2l0dWRlIG9yIGxhdGl0dWRlIHZhbHVlczoKCmBgYHtyfQp3dF9JQSA8LSBmaWx0ZXIod3RfSUEsICEgaXMubmEoeGxvbmcpLCAhIGlzLm5hKHlsYXQpKQpgYGAKClNvbWUgbWlzc2luZyB5ZWFyIHZhbHVlcyBhcmUgZW5jb2RlZCBhcyAtOTk5OTsgcmVwbGFjZSB0aGVzZSB3aXRoIGBOQWA6CgpgYGB7cn0Kd3RfSUEgPC0gbXV0YXRlKHd0X0lBLCBwX3llYXIgPSByZXBsYWNlKHBfeWVhciwgcF95ZWFyIDwgMCwgTkEpKQpgYGAKClRvIHNob3cgdGhlIGxvY2F0aW9ucyBvZiB3aW5kIHR1cmJpbmVzIG9uIGEgbWFwLCBsb2FkIHNvbWUgbWFwIGRhdGE6CgpgYGB7ciBpb3dhX3NmX21hcCwgZXZhbCA9IEZBTFNFfQppb3dhX3NmIDwtCiAgICBzZjo6c3RfYXNfc2YobWFwczo6bWFwKCJjb3VudHkiLCAiaW93YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFRSVUUpKQoKcCA8LSBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGRhdGEgPSBpb3dhX3NmKSArCiAgICBnZ3RoZW1lczo6dGhlbWVfbWFwKCkKcApgYGAKCmBgYHtyIGlvd2Ffc2ZfbWFwLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDh9CmBgYAoKTG9jYXRpb25zIGZvciBhbGwgd2luZCB0dXJiaW5lcyBpbiBpb3dhOgoKYGBge3Igd3QtSUEtYWxsLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX3BvaW50KGFlcyh4bG9uZywgeWxhdCksCiAgICAgICAgICAgICAgIGRhdGEgPSB3dF9JQSkKYGBgCmBgYHtyIHd0LUlBLWFsbCwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4fQpgYGAKClVzaW5nIGNvbG9yIHRvIHNob3cgd2hlbiB0aGUgd2luZCB0dXJiaW5lcyB3ZXJlICBidWlsdDoKCmBgYHtyIHd0LUlBLWNvbG9yLCBldmFsID0gRkFMU0V9CnllYXJfYnJrIDwtIGMoMCwgMjAwNSwgMjAxMCwgMjAxNSwgMjAyMCkKeWVhcl9sYWIgPC0gYygiYmVmb3JlIDIwMDUiLAogICAgICAgICAgICAgICIyMDA1LTIwMDkiLAogICAgICAgICAgICAgICIyMDEwLTIwMTQiLAogICAgICAgICAgICAgICIyMDE1LTIwMjAiKQp3dF9JQSA8LQogICAgbXV0YXRlKHd0X0lBLAogICAgICAgICAgIHllYXIgPSBjdXQocF95ZWFyLAogICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9icmssCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSB5ZWFyX2xhYiwKICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0ID0gRkFMU0UpKQpwICsgZ2VvbV9wb2ludChhZXMoeGxvbmcsCiAgICAgICAgICAgICAgICAgICB5bGF0LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyKSwKICAgICAgICAgICAgICAgZGF0YSA9IHd0X0lBLAogICAgICAgICAgICAgICBzaXplID0gMykKYGBgCmBgYHtyIHd0LUlBLWNvbG9yLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDh9CmBgYAoKYGBge3IgZXZhbCA9IEZBTFNFLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpwIDwtIGdncGxvdCgpICsgZ2VvbV9zZihkYXRhID0gaW93YV9zZikgKyBnZ3RoZW1lczo6dGhlbWVfbWFwKCkKcCArIGdlb21fcG9pbnQoYWVzKHhsb25nLCB5bGF0KSwgZGF0YSA9IHd0X0lBKQoKd3RfSUFfc2YgPC0gc2Y6OnN0X2FzX3NmKHd0X0lBLCBjb29yZHMgPSBjKCJ4bG9uZyIsICJ5bGF0IiksIGNycyA9IDQzMjYpCgpwICsgZ2VvbV9zZihkYXRhID0gZmlsdGVyKHd0X0lBX3NmLCB5ZWFyIDw9IDIwMjApKQoKbGlicmFyeShnZ2FuaW1hdGUpCnBhIDwtIHAgKyBnZW9tX3NmKGRhdGEgPSB3dF9JQV9zZikgKwogICAgdHJhbnNpdGlvbl9tYW51YWwoeWVhciwgY3VtdWxhdGl2ZSA9IFRSVUUpICsKICAgIGxhYnModGl0bGUgPSAiV2luZCB0dXJiaW5lcyBpbiBJb3dhIiwKICAgICAgICAgc3VidGl0bGUgPSAiWWVhciA9IHtjdXJyZW50X2ZyYW1lfSIpCmFuaW1fc2F2ZSgiZm9vLmdpZiIsIGFuaW1hdGUocGEsIGZwcyA9IDEwLCBuZnJhbWVzID0gMTAwKSkKYGBgCgojIyMgQ2FuY2VyIE1hcAoKVGhlIHdlYnNpdGUgPGh0dHA6Ly93d3cuY2FuY2VyLXJhdGVzLmluZm8vaWE+IHByb3ZpZGVzIGRhdGEgb24KY2FuY2VyIGluY2lkZW5jZSBmb3IgYSBudW1iZXIgb2YgZGlmZmVyZW50IGNhbmNlcnMgaW4gSW93YS4KCjwhLS0gIyBub2xpbnQgc3RhcnQgLS0+CgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpjYW5jZXJfZGF0YV9maWxlIDwtIGhlcmVfcmVsKCJkYXRhL0ludmFzaXZlLUNhbmNlci1JbmNpZGVuY2UtUmF0ZXMtYnktQ291bnR5LWluLUlvd2EtTHVuZy1hbmQtQnJvbmNodXMtMjAxMS5jc3YiKQpgYGAKCjwhLS0gIyBub2xpbnQgZW5kIC0tPgoKVGhlIGRhdGEgZm9yIGx1bmcgYW5kIGJyb25jaHVzIGNhbmNlciBpbiAyMDExIGFyZSBhdmFpbGFibGUgaW4gYSBbY3N2CmZpbGVdKGByIGNhbmNlcl9kYXRhX2ZpbGVgKSBpbiB0aGUgcHJvamVjdC4KCldlIGNhbiByZWFkIHRoZSBmaWxlIHdpdGggYHJlYWRfY3N2YCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UuCgpMb29raW5nIGF0IHRoZSBmaWxlIHNob3dzIHNvbWUgdGhpbmdzIHRoYXQgbmVlZCB0byBiZSBjbGVhbmVkIHVwOgoKKiBUd28gaGVhZGVyIGxpbmVzIGF0IHRoZSBiZWdpbm5pbmcuCgoqIFNvbWUgZm9vdGVyIGxpbmVzLgoKKiBTb21lIHZhbHVlcyBjb2RlcyBhcyBgfmAuCgpUaGUgaGVhZGVyIGNhbiBiZSBoYW5kbGVkIGJ5IHVzaW5nIGBza2lwID0gMmAgaW4gdGhlIGByZWFkX2NzdmAgY2FsbDoKCjwhLS0gIyBub2xpbnQgc3RhcnQgLS0+CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpmbmFtZSA8LSBoZXJlOjpoZXJlKCJkYXRhL0ludmFzaXZlLUNhbmNlci1JbmNpZGVuY2UtUmF0ZXMtYnktQ291bnR5LWluLUlvd2EtTHVuZy1hbmQtQnJvbmNodXMtMjAxMS5jc3YiKQpkIDwtIHJlYWRfY3N2KGZuYW1lLCBza2lwID0gMikKaGVhZChkKQpgYGAKCjwhLS0gIyBub2xpbnQgZW5kIC0tPgoKTGV0J3MgZm9jdXMgb24gYSBmZXcgdmFyaWFibGVzIGFuZCBnaXZlIHRoZW0gbW9yZSBjb252ZW5pZW50IG5hbWVzOgoKYGBge3J9CmQgPC0gc2VsZWN0KGQsIGNvdW50eSA9IDEsIHBvcHVsYXRpb24gPSAyLCBjb3VudCA9IDMpCmBgYAoKVGhlIGZvb3RlciBuZWVkcyB0byBiZSByZW1vdmVkOgoKYGBge3J9CnRhaWwoZCkKYGBgCgpPbmUgd2F5IHRvIHJlbW92ZSB0aGUgZm9vdGVyOgoKYGBge3J9CmQgPC0gZmlsdGVyKGQsICEgaXMubmEocG9wdWxhdGlvbikpCmQgPC0gZmlsdGVyKGQsIGNvdW50eSAhPSAiU1RBVEUiKQpgYGAKCkNoYW5naW5nIGBjb3VudGAgdG8gbnVtZXJpYyBjaGFuZ2VzIHRoZSBgfmAgZW50cmllcyB0byBtaXNzaW5nIHZhbHVlcwooYE5BYCkgdmFsdWVzOgoKYGBge3J9CmQgPC0gbXV0YXRlKGQsIGNvdW50ID0gYXMubnVtZXJpYyhjb3VudCkpCmBgYAoKSW4gdGhpcyBjYXNlIHRoZXJlIGFyZSBubyB6ZXJvIGNhc2UgdmFsdWVzOyB0d28gd2F5cyB0byBjaGVjazoKCmBgYHtyfQpjb3VudChkLCBjb3VudCA9PSAwKQpgYGAKYGBge3J9CmFueShkJGNvdW50ID09IDAsIG5hLnJtID0gVFJVRSkKYGBgCgpJdCBfbWlnaHRfIGJlIHJlYXNvbmFibGUgdG8gYXNzdW1lIHRoZXNlIHZhbHVlcyB3aGVyZSB6ZXJvLCBzbyByZXBsYWNlCnRoZW0gd2l0aCB6ZXJvczoKCmBgYHtyfQpkIDwtIHJlcGxhY2VfbmEoZCwgbGlzdChjb3VudCA9IDApKQpgYGAKCkEgX2Nob3JvcGxldGggbWFwXyB1c2VzIGNvbG9yIG9yIHNoYWRpbmcgdG8gcmVwcmVzZW50IHZhbHVlcyBtZWFzdXJlZApmb3IgZGlmZmVyZW50IGdlb2dyYXBoaWMgcmVnaW9ucy4KCldlIHdpbGwgbmVlZCB0byBtZXJnZSwgb3IgX2xlZnQgam9pbl8sIHRoZSBjYW5jZXIgZGF0YSB3aXRoIHRoZSBtYXAKZGF0ZSB3ZSBsb2FkZWQgZm9yIHRoZSB3aW5kIHR1cmJpbmUgbWFwLgoKVGhpcyByZXF1aXJlcyBhIF9rZXlfIG9uIHdoaWNoIHRvIG1hdGNoIHRoZSByZWNvcmRzIGluIHRoZSBjYW5jZXIgZGF0YQphbmQgdGhlIG1hcCBkYXRhLgoKRm9yIElvd2EgdGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBjb3VudHkgbmFtZSwgYnV0IHNvbWUgY2FyZSBpcyBuZWVkZWQ6CgpgYGB7cn0KZCRjb3VudHlbMV0KaW93YV9zZiRJRFsxXQpgYGAKCkZpeGluZyBjYXNlIGRpZmZlcmVuY2VzIGFuZCBkcm9wcGluZyB0aGUgYGlvd2EsYCBwcmVmaXg6CgpgYGB7cn0KZCA8LSBtdXRhdGUoZCwgY25hbWUgPSBjb3VudHksIGNvdW50eSA9IHRvbG93ZXIoY291bnR5KSkKaW93YV9zZiA8LSBtdXRhdGUoaW93YV9zZiwgSUQgPSBzdWIoImlvd2EsIiwgIiIsIElEKSkKaW93YV9zZiA8LSByZW5hbWUoaW93YV9zZiwgY291bnR5ID0gSUQpCmBgYAoKU3RpbGwgbm90IHF1aXRlIHRoZXJlOgoKYGBge3J9CnNldGRpZmYoZCRjb3VudHksIGlvd2Ffc2YkY291bnR5KQpzZXRkaWZmKGlvd2Ffc2YkY291bnR5LCBkJGNvdW50eSkKYGBgCgpEcm9wIHRoZSBhcG9zdHJvcGhlIGluIE8nQnJpZW46CgpgYGB7cn0KZCA8LSBtdXRhdGUoZCwgY291bnR5ID0gc3ViKCInIiwgIiIsIGNvdW50eSkpCgpzZXRkaWZmKGQkY291bnR5LCBpb3dhX3NmJGNvdW50eSkKc2V0ZGlmZihpb3dhX3NmJGNvdW50eSwgZCRjb3VudHkpCmBgYAoKRGVmaW5lIGByYXRlMUtgIHZhcmlhYmxlIGFzIHRoZSBudW1iZXIgb2YgY2FzZXMgcGVyIDEwMDAgaW5oYWJpdGFudHMKYW5kIGxlZnQgam9pbiB0aGUgY2FuY2VyIGRhdGEgdG8gdGhlIG1hcCBkYXRhOgoKYGBge3J9CmQgPC0gbXV0YXRlKGQsIHJhdGUxSyA9IDEwMDAgKiAoY291bnQgLyBwb3B1bGF0aW9uKSkKbWQgPC0gbGVmdF9qb2luKGlvd2Ffc2YsIGQsICJjb3VudHkiKQpoZWFkKG1kKQpgYGAKCkEgc2ltcGxlIG1hcDoKCmBgYHtyIGNhbmNlci1tYXAtMSwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobWQpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSByYXRlMUspKQpgYGAKYGBge3IgY2FuY2VyLW1hcC0xLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDh9CmBgYAoKQW4gaW1wcm92ZWQgdmVyc2lvbjoKCmBgYHtyIGNhbmNlci1tYXAtMiwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHZpcmlkaXMpCmdncGxvdChtZCkgKwogICAgZ2VvbV9zZihhZXMoZmlsbCA9IHJhdGUxSyksCiAgICAgICAgICAgIGNvbG9yID0gImdyZXkiKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJSYXRlIHBlciAxMDAwIikgKwogICAgdGhlbWVfbWFwKCkKYGBgCmBgYHtyIGNhbmNlci1tYXAtMiwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBtZXNzYWdlID0gRkFMU0V9CmBgYAoKQSBzaW1wbGUgaW50ZXJhY3RpdmUgdmVyc2lvbiB1c2luZyBbYHBsb3RseWBdKGh0dHBzOi8vcGxvdC5seS9yLyk6CgpgYGB7ciBjYW5jZXItbWFwLXBsb3RseSwgZXZhbCA9IEZBTFNFfQptZGwgPC0gbXV0YXRlKG1kLAogICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoY25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChyYXRlMUssIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcbiIpKQpwIDwtIGdncGxvdChtZGwpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSByYXRlMUssCiAgICAgICAgICAgICAgICB0ZXh0ID0gbGFiZWwpLAogICAgICAgICAgICBjb2xvciA9ICJncmV5IikgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWUgPSAiUmF0ZSBwZXIgMTAwMCIpICsKICAgIHRoZW1lX21hcCgpCgpwbG90bHk6OmdncGxvdGx5KHAsIHRvb2x0aXAgPSAidGV4dCIpCmBgYApgYGB7ciBjYW5jZXItbWFwLXBsb3RseSwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4fQpgYGAKClRoZSBbYGxlYWZsZXRgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvKSBwYWNrYWdlIHN1cHBvcnRzCm1vcmUgc29waGlzdGljYXRlZCBpbnRlcmFjdGl2ZSBtYXBzOgoKPCEtLSBodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC9sZWdlbmRzLmh0bWwtLT4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpsaWJyYXJ5KGxlYWZsZXQpCnBhbCA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJ2aXJpZGlzIiwgZG9tYWluID0gbWQkcmF0ZTFLKQpsYWIgPC0gbGFwcGx5KHBhc3RlMChtZCRjbmFtZSwgIjxCUj4iLAogICAgICAgICAgICAgICAgICAgICAiUmF0ZTogIiwgcm91bmQobWQkcmF0ZTFLLCAxKSwgIjxCUj4iLAogICAgICAgICAgICAgICAgICAgICAiUG9wOiAiLCBzY2FsZXM6OmNvbW1hKG1kJHBvcHVsYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjdXJhY3kgPSAxKSksCiAgICAgICAgICAgICAgaHRtbHRvb2xzOjpIVE1MKQpsZWFmbGV0KHNmOjpzdF90cmFuc2Zvcm0obWQsIDQzMjYpKSAlPiUKICAgIGFkZFBvbHlnb25zKHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgICBjb2xvciA9ICJncmV5IiwKICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH4gcGFsKHJhdGUxSyksCiAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgICBoaWdobGlnaHRPcHRpb25zID0gaGlnaGxpZ2h0T3B0aW9ucyhjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJpbmdUb0Zyb250ID0gVFJVRSksCiAgICAgICAgICAgICAgICBsYWJlbCA9IGxhYikgJT4lCiAgICBhZGRMZWdlbmQocGFsID0gcGFsLCB2YWx1ZXMgPSB+IHJhdGUxSywgb3BhY2l0eSA9IDEpCmBgYAoKCiMjIyBVbmVtcGxveW1lbnQgTWFwCgpbTG9jYWwgQXJlYSBVbmVtcGxveW1lbnQgU3RhdGlzdGljcyBwYWdlXShodHRwczovL3d3dy5ibHMuZ292L2xhdS8pCmZyb20gdGhlIEJ1cmVhdSBvZiBMYWJvciBTdGF0aXN0aWNzIG1ha2VzIGF2YWlsYWJsZSBjb3VudHktbGV2ZWwKbW9udGhseSB1bmVtcGxveW1lbnQgZGF0YSBmb3IgYSAxNC1tb250aCB3aW5kb3cuCgpUaGUgZmlsZSBmb3IgRmVicnVhcnkgMjAyMCB0aHJvdWdoIE1hcmNoIDIwMjEgaXMgYXZhaWxhYmxlIGlzCmF2YWlsYWJsZSBhdAo8aHR0cDovL3d3dy5zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL2xhdXMvbGF1Y250eWN1cjE0LTIwMjAudHh0PiBhbmQKaW4gdGhlIHByb2plY3QgZGF0YSBmb2xkZXIuCgo8ZGl2IGNsYXNzID0gIm5vdGUiPgpUaGlzIGZpbGUgaXMgYSB0ZXh0IGZpbGUgYnV0IHVzZXMgYSBub24tc3RhbmRhcmQgc2VwYXJhdG9yLgoKSXQgaXMgZGVzaWduZWQgZm9yIGh1bWFuIHJlYWRhYmlsaXR5IGFuZCB1c2VzCmEgY29tbWEgYXMgYSBfdGhvdXNhbmRzIHNlcGFyYXRvcl8gb3IgX2dyb3VwaW5nIG1hcmtfLgoKSXQgYWxzbyBpbmNsdWRlcyBoZWFkZXIgYW5kIGZvb3RlciBpbmZvcm1hdGlvbi4KCkl0IGlzIHN0aWxsIHJlYXNvbmFibHkgZWFzeSB0byByZWFkIGluLgo8L2Rpdj4KCk9uZSB3YXkgdG8gcmVhZCB0aGUgZGF0YSBpbnRvIFIgaXM6CgpgYGB7cn0KbGF1c1VSTCA8LSBoZXJlOjpoZXJlKCJkYXRhL2xhdWNudHljdXIxNC0yMDIwLnR4dCIpCmxhdXNVUyA8LSByZWFkLnRhYmxlKGxhdXNVUkwsCiAgICAgICAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIkxBVVNBcmVhQ29kZSIsICJTdGF0ZSIsICJDb3VudHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXRsZSIsICJQZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYWJvckZvcmNlIiwgIkVtcGxveWVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW5lbXBsb3llZCIsICJVbmVtcFJhdGUiKSwKICAgICAgICAgICAgICAgICAgICAgcXVvdGUgPSAnIicsIHNlcCA9ICJ8Iiwgc2tpcCA9IDYsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgc3RyaXAud2hpdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gVFJVRSkKZm9vdHN0YXJ0IDwtIGdyZXAoIi0tLS0tLSIsIGxhdXNVUyRMQVVTQXJlYUNvZGUpCmxhdXNVUyA8LSBsYXVzVVNbMSA6IChmb290c3RhcnQgLSAxKSwgXQpgYGAKCkl0IG1heSBiZSB1c2VmdWwgdG8gYmUgYWJsZSB0byBhY2Nlc3MgdGhlIGNvdW50eSBuYW1lIGFuZCBzdGF0ZSBuYW1lCnNlcGFyYXRlbHk6CgpgYGB7cn0KbGF1c1VTIDwtIHNlcGFyYXRlKGxhdXNVUywgVGl0bGUsIGMoImNuYW1lIiwgInNjb2RlIiksCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiLCAiLCBmaWxsID0gInJpZ2h0IikKYGBgCgpDaGVjayB0aGUgdmFyaWFibGUgdHlwZXM6CgpgYGB7cn0Kc2FwcGx5KGxhdXNVUywgY2xhc3MpCmBgYAoKVGhlIGBVbmVtcFJhdGVgIHZhcmlhYmxlIGlzIHJlYWQgYXMgY2hhcmFjdGVyIGRhdGEgYmVjYXVzZSBvZiBtaXNzaW5nCnZhbHVlIGVuY29kaW5nLCBzbyBuZWVkcyB0byBiZSBjb252ZXJ0ZWQgdG8gbnVtZXJpYzoKCmBgYHtyfQpsYXVzVVMgPC0gbXV0YXRlKGxhdXNVUywgVW5lbXBSYXRlID0gYXMubnVtZXJpYyhVbmVtcFJhdGUpKQpgYGAKCkNoZWNrIGZvciBtaXNzaW5nIHZhbHVlczoKCmBgYHtyfQpzZWxlY3RfaWYobGF1c1VTLCBhbnlOQSkgJT4lIG5hbWVzKCkKYGBgCgpUaGUgc3RhdGUgY29kZSBpcyBtaXNzaW5nIGZvciB0aGUgRGlzdHJpY3Qgb2YgQ29sdW1iaWE6CgpgYGB7cn0Kc2VsZWN0KGxhdXNVUywgY25hbWUsIHNjb2RlKSAlPiUKICAgIGZpbHRlcihpcy5uYShzY29kZSkpICU+JQogICAgdW5pcXVlKCkKYGBgCgo8IS0tCk1pc3NpbmcgdmFsdWVzIGZvciBgVW5lbXBSYXRlYCBhcmUgYWxsIGZvciBQdWVydG8gUmljbyBhbmQgU2VwdGVtYmVyCjIwMTcuIEh1cnJpY2FuZSBNYXJpYSBtYWRlIGxhbmRmYWxsIG9uIFNlcHRlbWJlciAyMC4gLS0+CgpNYXJjaCBhbmQgQXByaWwgMjAyMCBudW1iZXJzIHdlcmUgbm90IGF2YWlsYWJsZSBmb3IgUHVlcnRvIFJpY286CgpgYGB7cn0Kc2VsZWN0KGxhdXNVUywgc2NvZGUsIFBlcmlvZCwgVW5lbXBSYXRlKSAlPiUKICAgIGZpbHRlcihpcy5uYShVbmVtcFJhdGUpKSAlPiUKICAgIHVuaXF1ZSgpCmBgYAoKVG8gY29tcHV0ZSB0aGUgbmF0aW9uYWwgbW9udGhseSB1bmVtcGxveW1lbnQgcmF0ZXMgb3ZlciB0aGlzIHBlcmlvZCB3ZQpuZWVkIHNvbWUgbW9yZSBkYXRhIGNsZWFuaW5nOgoKYGBge3J9CmxhdXNVUyA8LSBtdXRhdGUobGF1c1VTLAogICAgICAgICAgICAgICAgIFBlcmlvZCA9IGZjdF9pbm9yZGVyKFBlcmlvZCksCiAgICAgICAgICAgICAgICAgTGFib3JGb3JjZSA9IGFzLm51bWVyaWMoZ3N1YigiLCIsICIiLCBMYWJvckZvcmNlKSksCiAgICAgICAgICAgICAgICAgVW5lbXBsb3llZCA9IGFzLm51bWVyaWMoZ3N1YigiLCIsICIiLCBVbmVtcGxveWVkKSkpCmBgYAoKVW5lbXBsb3ltZW50IGR1cmluZyB0aGlzIHBlcmlvZCB3YXMgYWZmZWN0ZWQgc2lnbmlmaWNhbnRseSBieSB0aGUKQ09WSUQtMTkgcGFuZGVtaWMuCgpBIHBsb3Qgc2hvd3MgYSBsYXJnZSBzcGlrZSBpbiBBcHJpbCAyMDIwOgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpncm91cF9ieShsYXVzVVMsIFBlcmlvZCkgJT4lCiAgICBzdW1tYXJpemUoVW5lbXBsb3llZCA9IHN1bShVbmVtcGxveWVkLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIExhYm9yRm9yY2UgPSBzdW0oTGFib3JGb3JjZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICBVbmVtcFJhdGUgPSAxMDAgKiAoVW5lbXBsb3llZCAvIExhYm9yRm9yY2UpKSAlPiUKICAgIGdncGxvdChhZXMoUGVyaW9kLCBVbmVtcFJhdGUsIGdyb3VwID0gMSkpICsKICAgIGdlb21fbGluZSgpCmBgYAoKQSBjaG9yb3BsZXRoIG1hcCBjYW4gYmUgdXNlZCB0byBsb29rIGF0IGhvdyB0aGUgaW1wYWN0IHdhcyBkaXN0cmlidXRlZAphY3Jvc3MgdGhlIGNvdW50cnkuCgpUbyBzaG93IHVuZW1wbG95bWVudCByYXRlcyBvbiBhIG1hcCB3ZSBuZWVkIHRvIG1lcmdlIHRoZSB1bmVtcGxveW1lbnQgZGF0YQp3aXRoIG1hcCBkYXRhLgoKVG8gbWF0Y2ggY291bnR5IHVuZW1wbG95bWVudCBkYXRhIGFuZCBjb3VudHkgc2hhcGUgZGF0YSBpdCBpcyBzYWZlciB0bwp1c2UgdGhlIG51bWVyaWMgW0ZJUFMgY291bnR5CmNvZGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZJUFNfY291bnR5X2NvZGUpLiBUaGlzIGNhbiBiZQphZGRlZCB3aXRoCgpgYGB7cn0KbGF1c1VTIDwtIG11dGF0ZShsYXVzVVMsIGZpcHMgPSBTdGF0ZSAqIDEwMDAgKyBDb3VudHkpCmBgYAoKU2hhcGUgZGF0YSBmb3IgVVMgY291bnRpZXMgY2FuIGJlIG9idGFpbmVkIGZyb20gYSBudW1iZXIgb2Ygc291cmNlcyBpbiBhCm51bWJlciBvZiBkaWZmZXJlbnQgZm9ybWF0cy4KCkhlcmUgaXMgb25lIGFwcHJvYWNoOgoKYGBge3J9CmNvdW50aWVzX3NmIDwtIHNmOjpzdF9hc19zZihtYXBzOjptYXAoImNvdW50eSIsIHBsb3QgPSBGQUxTRSwgZmlsbCA9IFRSVUUpKQpjb3VudHkuZmlwcyA8LQogICAgbXV0YXRlKG1hcHM6OmNvdW50eS5maXBzLCBwb2x5bmFtZSA9IHN1YigiOi4qIiwgIiIsIHBvbHluYW1lKSkgJT4lCiAgICB1bmlxdWUoKQpjb3VudGllc19zZiA8LSBsZWZ0X2pvaW4oY291bnRpZXNfc2YsIGNvdW50eS5maXBzLCBjKCJJRCIgPSAicG9seW5hbWUiKSkKc3RhdGVzX3NmIDwtIHNmOjpzdF9hc19zZihtYXBzOjptYXAoInN0YXRlIiwgcGxvdCA9IEZBTFNFLCBmaWxsID0gVFJVRSkpCmBgYAoKU29tZSBzdW1tYXJpZXMgb3ZlciB0aGUgcGVyaW9kIGNhbiBiZSBjb21wdXRlZCBhcwoKYGBge3J9CnN1bW1hcnlVUyA8LSBncm91cF9ieShsYXVzVVMsIENvdW50eSwgU3RhdGUsIGZpcHMpICU+JQogICAgc3VtbWFyaXplKGF2Z191bmVtcCA9IG1lYW4oVW5lbXBSYXRlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIG1heF91bmVtcCA9IG1heChVbmVtcFJhdGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgYXByX3VuZW1wID0gVW5lbXBSYXRlW1BlcmlvZCA9PSAiQXByLTIwIl0pICU+JQogICAgdW5ncm91cCgpCmhlYWQoc3VtbWFyeVVTKQpgYGAKCkEgY2hvcm9wbGV0aCBtYXAgb2YgdGhlIEFwcmlsIDIwMjAgdW5lbXBsb3ltZW50IHJhdGVzOgoKYGBge3IsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KbGVmdF9qb2luKGNvdW50aWVzX3NmLCBzdW1tYXJ5VVMsICJmaXBzIikgJT4lCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGFlcyhmaWxsID0gYXByX3VuZW1wKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWUgPSAiUmF0ZSIsIG5hLnZhbHVlID0gInJlZCIpICsKICAgIHRoZW1lX21hcCgpICsKICAgIGdlb21fc2YoZGF0YSA9IHN0YXRlc19zZiwgY29sID0gImdyZXkiLCBmaWxsID0gTkEpCmBgYAoKVXNpbmcgYSB2ZXJ5IHZpc2libGUgY29sb3IgZm9yIG1pc3NpbmcgZGF0YSBpcyB1c2VmdWwsIGF0IGxlYXN0IGR1cmluZwpleHBsb3JhdGlvbi4KCmBhbnRpX2pvaW5gIGNhbiBzaG93IHRoZSBjb3VudHkgZ2VvbWV0cnkgdGhhdCBkb2VzIG5vdCBoYXZlIGFuIGVudHJ5CmluIHRoZSB1bmVtcGxveW1lbnQgZGF0YToKCmBgYHtyfQphbnRpX2pvaW4oY291bnRpZXNfc2YsIHN1bW1hcnlVUywgImZpcHMiKQpgYGAKClNoYW5ub24gQ291bnR5LCBTRCAoRklQUyA0NjExMyksIHdhcyByZW5hbWVkIHRvIFtPZ2xhbGEgTGFrb3RhIENvdW50eV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvT2dsYWxhX0xha290YV9Db3VudHksX1NvdXRoX0Rha290YSkgaW4gSnVuZSAyMDE1IGFuZCBnaXZlbiBhIG5ldyBGSVBTIGNvZGUsIDQ2MTAyLgoKVGhlIGdlb21ldHJ5IGRhdGEgdGFibGUgbmVlZHMgdG8gYmUgdXBkYXRlZDoKCmBgYHtyfQpjb3VudGllc19zZiA8LSBtdXRhdGUoY291bnRpZXNfc2YsIGZpcHMgPSByZXBsYWNlKGZpcHMsIGZpcHMgPT0gNDYxMTMsIDQ2MTAyKSkKYGBgCgpXaXRoIHRoZSB1cGRhdGVkIGRhdGEgdGhlIG1hcCBpcyBub3cgY29tcGxldGU6CgpgYGB7ciwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpsZWZ0X2pvaW4oY291bnRpZXNfc2YsIHN1bW1hcnlVUywgImZpcHMiKSAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSBhcHJfdW5lbXApKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJSYXRlIiwgbmEudmFsdWUgPSAicmVkIikgKwogICAgdGhlbWVfbWFwKCkgKwogICAgZ2VvbV9zZihkYXRhID0gc3RhdGVzX3NmLCBjb2wgPSAiZ3JleSIsIGZpbGwgPSBOQSkKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBldmFsID0gRkFMU0V9CmdncG9seTJzZiA8LSBmdW5jdGlvbihwb2x5LCBjb29yZHMgPSBjKCJsb25nIiwgImxhdCIpLAogICAgICAgICAgICAgICAgICAgICAgaWQgPSAiZ3JvdXAiLCByZWdpb24gPSAicmVnaW9uIiwgY3JzID0gNDMyNikgewogICAgc2Y6OnN0X2FzX3NmKHBvbHksIGNvb3JkcyA9IGNvb3JkcywgY3JzID0gY3JzKSAlPiUKICAgIGdyb3VwX2J5KCEhIGFzLm5hbWUoaWQpLCAhISBhcy5uYW1lKHJlZ2lvbikpICU+JQogICAgc3VtbWFyaXplKGRvX3VuaW9uID0gRkFMU0UpICU+JQogICAgc2Y6OnN0X2Nhc3QoIlBPTFlHT04iKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIGdyb3VwX2J5KCEhIGFzLm5hbWUocmVnaW9uKSkgJT4lCiAgICBzdW1tYXJpemUoZG9fdW5pb24gPSBGQUxTRSkgJT4lCiAgICB1bmdyb3VwKCkKfQptX3NmIDwtIGdncG9seTJzZihzb2N2aXo6OmNvdW50eV9tYXAsIGMoImxvbmciLCAibGF0IiksICJncm91cCIsICJpZCIpCm1fc2YgPC0gbXV0YXRlKG1fc2YsIGZpcHMgPSBhcy5udW1lcmljKGlkKSkKbV9zZiA8LSBtdXRhdGUobV9zZiwgZmlwcyA9IHJlcGxhY2UoZmlwcywgZmlwcyA9PSA0NjExMywgNDYxMDIpKQpnZ3Bsb3QobV9zZikgKyBnZW9tX3NmKCkKYXUgPC0gZ3JvdXBfYnkobGF1c1VTLCBmaXBzKSAlPiUKICAgIHN1bW1hcml6ZShhdmdfdXIgPSBtZWFuKFVuZW1wUmF0ZSwgbmEucm0gPSBUUlVFKSkKbXUgPC0gZ3JvdXBfYnkobGF1c1VTLCBmaXBzKSAlPiUKICAgIHN1bW1hcml6ZShtYXhfdXIgPSBtYXgoVW5lbXBSYXRlLCBuYS5ybSA9IFRSVUUpKQpkYSA8LSBsZWZ0X2pvaW4obV9zZiwgYXUsICJmaXBzIikKZG0gPC0gbGVmdF9qb2luKG1fc2YsIG11LCAiZmlwcyIpCmdncGxvdChkYSwgYWVzKGZpbGwgPSBhdmdfdXIpKSArCiAgICBnZW9tX3NmKHNpemUgPSAwLjEpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lID0gIlJhdGUiLCBuYS52YWx1ZSA9ICJyZWQiKQpnZ3Bsb3QoZG0sIGFlcyhmaWxsID0gbWF4X3VyKSkgKwogICAgZ2VvbV9zZihzaXplID0gMC4xKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJSYXRlIiwgbmEudmFsdWUgPSAicmVkIikKZ2dwbG90KGxlZnRfam9pbihtX3NmLCBmaWx0ZXIobGF1c1VTLCBQZXJpb2QgPT0gIkFwci0yMCIpLCAiZmlwcyIpLAogICAgICAgYWVzKGZpbGwgPSBVbmVtcFJhdGUpKSArCiAgICBnZW9tX3NmKHNpemUgPSAwLjEpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lID0gIlJhdGUiLCBuYS52YWx1ZSA9ICJyZWQiKQpgYGAKCiMjIyBHYXBtaW5kZXIgQ2hpbGRob29kIE1vcnRhbGl0eSBEYXRhCgpUaGUgYGdhcG1pbmRlcmAgcGFja2FnZSBwcm92aWRlcyBhIHN1YnNldCBvZiB0aGUgZGF0YSBmcm9tIHRoZQpbR2FwbWluZGVyXShodHRwOi8vd3d3LmdhcG1pbmRlci5vcmcvKSB3ZWIgc2l0ZS4KCkFkZGl0aW9uYWwgZGF0YSBzZXRzIGFyZSBbYXZhaWxhYmxlXShodHRwOi8vd3d3LmdhcG1pbmRlci5vcmcvZGF0YS8pLgoKKiBBIGRhdGEgc2V0IG9uIGNoaWxkaG9vZCBtb3J0YWxpdHkgaXMgYXZhaWxhYmxlIGxvY2FsbHkgYXMgYSBbY3N2CiAgZmlsZV0oaHR0cDovL2hvbWVwYWdlLnN0YXQudWlvd2EuZWR1L35sdWtlL2RhdGEvZ2FwbWluZGVyLXVuZGVyNW1vcnRhbGl0eS5jc3YpCiAgb3IgYW4gW0V4Y2VsCiAgZmlsZV0oaHR0cDovL2hvbWVwYWdlLnN0YXQudWlvd2EuZWR1L35sdWtlL2RhdGEvZ2FwbWluZGVyLXVuZGVyNW1vcnRhbGl0eS54bHN4KS4KCiogVGhlIEV4Y2VsIGZpbGUgaXMgYWxzbyBhdmFpbGFibGUgaW4gdGhlIHByb2plY3QgZGF0YSBmb2xkZXIuCgoqIFRoZSBudW1iZXJzIHJlcHJlc2VudCBudW1iZXIgb2YgZGVhdGhzIHdpdGhpbiB0aGUgZmlyc3QgZml2ZSB5ZWFycwogIHBlciAxMDAwIGJpcnRocy4KCjxkaXYgY2xhc3M9Im5vdGUiPgpNYW55IHJlc2VhcmNoZXJzIGxpa2UgdG8gbWFuYWdlIHRoZWlyIGRhdGEgaW4gYSBzcHJlYWRzaGVldC4KCiogQmVpbmcgYWJsZSB0byByZWFkIHN1Y2ggYSBzaGVldCBkaXJlY3RseSBncmVhdGx5IGhlbHBzIGtlZXBpbmcgdGhlCiAgd29ya2Zsb3cgcmVwcm9kdWNpYmxlLgoKKiBNYW55IHNwcmVhZHNoZWV0cyBjb250YWluIGhlYWRlciwgZm9vdGVycywgYW5kIG90aGVyIGFubm90YXRpb25zIHRvCiAgYWlkIGEgaHVtYW4gdmlld2VyLgoKKiBBcyBsb25nIGFzIHRoZSBkYXRhIGFyZSBpbiBhIHJlY3Rhbmd1bGFyIHJlZ2lvbiBpdCBpcyB1c3VhbGx5IG5vdAogIGhhcmQgdG8gZXh0cmFjdCB0aGVtIHByb2dyYW1tYXRpY2FsbHkuCjwvZGl2PgoKTG9hZGluZyB0aGUgZGF0YToKCmBgYHtyfQpsaWJyYXJ5KHJlYWR4bCkKZ2NtIDwtIHJlYWRfZXhjZWwoaGVyZTo6aGVyZSgiZGF0YS9nYXBtaW5kZXItdW5kZXI1bW9ydGFsaXR5Lnhsc3giKSkKYGBgCgpBIGZpcnN0IGxvb2s6CgpgYGB7cn0KaGVhZChnY20sIDMpCmBgYAoKVGhpcyBkYXRhIHNldCBpcyBpbiBfd2lkZV8gZm9ybWF0LCB3aXRoIG9uZSBjb2x1bW4gcGVyIHllYXIuCgpBIF9sb25nXyB2ZXJzaW9uIHdpdGggYSB5ZWFyIGFuZCBhIHZhbHVlIGNvbHVtbiBpcyB1c2VmdWwgZm9yIHdvcmtpbmcKd2l0aCBgZ2dwbG90YC4KCkEgYmV0dGVyIGZpcnN0IHZhcmlhYmxlIG5hbWU6CgpgYGB7cn0KbmFtZXMoZ2NtKVsxXSA8LSAiY291bnRyeSIKYGBgCgpDb252ZXJ0IHRvIGxvbmcgZm9ybWF0OgoKYGBge3J9CnRnY20gPC0KICAgIHBpdm90X2xvbmdlcihnY20sIC0xLCBuYW1lc190byA9ICJ5ZWFyIiwgdmFsdWVzX3RvID0gInU1bW9ydCIpICU+JQogICAgbXV0YXRlKHllYXIgPSBhcy5udW1lcmljKHllYXIpKQpoZWFkKHRnY20sIDMpCmBgYAoKU29tZSBleHBsb3JhdGlvbnM6CgpgYGB7ciB1NS0xLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KHRnY20pICsKICAgIGdlb21fbGluZShhZXMoeWVhciwKICAgICAgICAgICAgICAgICAgdTVtb3J0LAogICAgICAgICAgICAgICAgICBncm91cCA9IGNvdW50cnkpLAogICAgICAgICAgICAgIGFscGhhID0gMC4zKQpwbG90bHk6OmdncGxvdGx5KHApCmBgYApgYGB7ciB1NS0xLCBlY2hvID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4fQpgYGAKClNvbWUgc2VsZWN0ZWQgY291bnRyaWVzOgoKYGBge3IgdTUtMiwgZXZhbCA9IEZBTFNFfQpjb3VudHJpZXMgPC0gYygiVW5pdGVkIFN0YXRlcyIsCiAgICAgICAgICAgICAgICJVbml0ZWQgS2luZ2RvbSIsCiAgICAgICAgICAgICAgICJHZXJtYW55IiwKICAgICAgICAgICAgICAgIkNoaW5hIiwKICAgICAgICAgICAgICAgIkVneXB0IikKZmlsdGVyKHRnY20sIGNvdW50cnkgJWluJSBjb3VudHJpZXMpICU+JQogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwKICAgICAgICAgICAgICAgICAgeSA9IHU1bW9ydCwKICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb3VudHJ5KSkKYGBgCmBgYHtyIHU1LTIsIGVjaG8gPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9CmBgYAoKRXhhbWluaW5nIHRoZSBtaXNzaW5nIHZhbHVlczoKCmBgYHtyIHU1LTMsIGV2YWwgPSBGQUxTRX0KdGdjbV9taXNzIDwtCiAgICBncm91cF9ieSh0Z2NtLCBjb3VudHJ5KSAlPiUKICAgIHN1bW1hcml6ZShhbnlOQSA9IGFueU5BKHU1bW9ydCkpICU+JQogICAgZmlsdGVyKGFueU5BKSAlPiUKICAgIHB1bGwoY291bnRyeSkKCnAgPC0gZmlsdGVyKHRnY20sCiAgICAgICAgICAgIGNvdW50cnkgJWluJSB0Z2NtX21pc3MpICU+JQogICAgZ2dwbG90KGFlcyh4ID0geWVhciwKICAgICAgICAgICAgICAgeSA9IHU1bW9ydCwKICAgICAgICAgICAgICAgZ3JvdXAgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKG5hLnJtID0gVFJVRSkgKwogICAgeGxpbShjKDE5NDAsIDIwMjApKQpwbG90bHk6OmdncGxvdGx5KHApCmBgYApgYGB7ciB1NS0zLCBlY2hvID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA3fQpgYGAK